Per op note for vaginal hysterectomy
Source: Berek & Novak's Gynecology, Chapter 27; Campbell-Walsh-Wein Urology, Chapter 68
Search for clinical images of vaginal hysterectomy surgical anatomy
vaginal hysterectomy surgical anatomy
vaginal hysterectomy operative steps cervix uterosacral ligament
vaginal hysterectomy colpotomy incision bladder dissection anterior cul de sac








Images sourced from peer-reviewed PMC clinical literature. All depict intraoperative anatomy for surgical education.
Create a PDF quick-reference guide on vaginal hysterectomy surgical planes and landmarks
mkdir -p /home/daytona/workspace/vaginal-hx-guide && ls /home/daytona/workspace/
~/vaginal-hx-guide/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import Flowable
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Circle, Polygon
from reportlab.graphics import renderPDF
import io
OUTPUT = "/home/daytona/workspace/vaginal-hx-guide/Vaginal_Hysterectomy_Quick_Reference.pdf"
# ─── Colour palette ────────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1A3A5C")
MID_BLUE = colors.HexColor("#2E6DA4")
LIGHT_BLUE = colors.HexColor("#D6E8F7")
ACCENT = colors.HexColor("#C0392B") # red for warnings / key structures
GOLD = colors.HexColor("#D4A017")
GREEN = colors.HexColor("#1E7B45")
PALE_GREEN = colors.HexColor("#E8F5EC")
PALE_AMBER = colors.HexColor("#FFF8E1")
PALE_RED = colors.HexColor("#FDECEA")
WHITE = colors.white
GREY_LIGHT = colors.HexColor("#F4F6F8")
GREY_MID = colors.HexColor("#BDC3C7")
TEXT_DARK = colors.HexColor("#1C2833")
# ─── Document setup ────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=1.8*cm, rightMargin=1.8*cm,
topMargin=2.2*cm, bottomMargin=2.0*cm,
title="Vaginal Hysterectomy – Surgical Planes & Landmarks",
author="Orris Medical Reference",
subject="Quick-Reference Guide"
)
W, H = A4
CONTENT_W = W - 3.6*cm
styles = getSampleStyleSheet()
# ─── Custom styles ─────────────────────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
sTitle = S("sTitle",
fontName="Helvetica-Bold", fontSize=22, textColor=WHITE,
spaceAfter=4, spaceBefore=0, alignment=TA_CENTER)
sSubtitle = S("sSubtitle",
fontName="Helvetica", fontSize=11, textColor=LIGHT_BLUE,
spaceAfter=2, alignment=TA_CENTER)
sH1 = S("sH1",
fontName="Helvetica-Bold", fontSize=13, textColor=WHITE,
spaceBefore=6, spaceAfter=4, leftIndent=6)
sH2 = S("sH2",
fontName="Helvetica-Bold", fontSize=10.5, textColor=DARK_BLUE,
spaceBefore=8, spaceAfter=3)
sH3 = S("sH3",
fontName="Helvetica-Bold", fontSize=9.5, textColor=MID_BLUE,
spaceBefore=5, spaceAfter=2)
sBody = S("sBody",
fontName="Helvetica", fontSize=9, textColor=TEXT_DARK,
spaceAfter=3, leading=13, alignment=TA_JUSTIFY)
sBullet = S("sBullet",
fontName="Helvetica", fontSize=9, textColor=TEXT_DARK,
spaceAfter=2, leading=13, leftIndent=14, firstLineIndent=-10)
sBold = S("sBold",
fontName="Helvetica-Bold", fontSize=9, textColor=TEXT_DARK,
spaceAfter=2, leading=13, leftIndent=14, firstLineIndent=-10)
sWarning = S("sWarning",
fontName="Helvetica-Bold", fontSize=8.5, textColor=ACCENT,
spaceAfter=2, leading=12, leftIndent=10)
sCaption = S("sCaption",
fontName="Helvetica-Oblique", fontSize=8, textColor=colors.HexColor("#555555"),
spaceAfter=2, alignment=TA_CENTER)
sTableHdr = S("sTableHdr",
fontName="Helvetica-Bold", fontSize=8.5, textColor=WHITE,
alignment=TA_CENTER, leading=11)
sTableCell = S("sTableCell",
fontName="Helvetica", fontSize=8.5, textColor=TEXT_DARK,
leading=12, alignment=TA_LEFT)
sTableCellBold = S("sTableCellBold",
fontName="Helvetica-Bold", fontSize=8.5, textColor=TEXT_DARK,
leading=12, alignment=TA_LEFT)
sFooter = S("sFooter",
fontName="Helvetica-Oblique", fontSize=7.5, textColor=colors.HexColor("#7F8C8D"),
alignment=TA_CENTER)
# ─── Flowable helpers ──────────────────────────────────────────────────────────
class ColorBox(Flowable):
"""Rounded background box for section headers."""
def __init__(self, text_para, bg_color, width, height=26, radius=4):
Flowable.__init__(self)
self.text_para = text_para
self.bg_color = bg_color
self.width = width
self.height = height
self.radius = radius
def draw(self):
c = self.canv
c.setFillColor(self.bg_color)
c.roundRect(0, 0, self.width, self.height, self.radius, fill=1, stroke=0)
self.text_para.wrapOn(c, self.width - 12, self.height)
self.text_para.drawOn(c, 6, 6)
def wrap(self, availW, availH):
return self.width, self.height
class HLine(Flowable):
def __init__(self, width, color=GREY_MID, thickness=0.6):
Flowable.__init__(self)
self.width = width
self.color = color
self.thickness = thickness
def draw(self):
self.canv.setStrokeColor(self.color)
self.canv.setLineWidth(self.thickness)
self.canv.line(0, 0, self.width, 0)
def wrap(self, w, h):
return self.width, self.thickness + 1
def section_header(text, color=DARK_BLUE, number=None):
label = f"{number}. {text}" if number else text
p = Paragraph(label, sH1)
return [ColorBox(p, color, CONTENT_W, height=28), Spacer(1, 6)]
def h2(text): return [Paragraph(text, sH2)]
def h3(text): return [Paragraph(text, sH3)]
def body(text): return [Paragraph(text, sBody)]
def bullet(text, bold_prefix=None):
if bold_prefix:
return [Paragraph(f"• <b>{bold_prefix}:</b> {text}", sBullet)]
return [Paragraph(f"• {text}", sBullet)]
def warn(text): return [Paragraph(f"⚠ {text}", sWarning)]
def sp(n=4): return [Spacer(1, n)]
def hline(): return [HLine(CONTENT_W)]
# ─── Anatomy diagram ───────────────────────────────────────────────────────────
def make_anatomy_diagram():
"""Simple schematic of pelvic surgical planes — sagittal cross-section view."""
d = Drawing(CONTENT_W, 210)
cx = CONTENT_W / 2
# Background
d.add(Rect(0, 0, CONTENT_W, 210, fillColor=colors.HexColor("#F0F4F8"), strokeColor=None))
# ── Sacrum (posterior wall) ──
d.add(Polygon([cx+60,10, cx+110,10, cx+120,80, cx+80,160, cx+60,170],
fillColor=colors.HexColor("#D5C4A1"), strokeColor=colors.HexColor("#9E8B60"), strokeWidth=1))
d.add(String(cx+72, 85, "Sacrum", fontSize=7.5, fillColor=TEXT_DARK, fontName="Helvetica"))
# ── Rectum ──
d.add(Rect(cx+30, 15, 28, 90, rx=10, ry=10,
fillColor=colors.HexColor("#F5CBA7"), strokeColor=colors.HexColor("#CA6F1E"), strokeWidth=1))
d.add(String(cx+34, 58, "Rectum", fontSize=7, fillColor=TEXT_DARK, fontName="Helvetica"))
# ── Posterior cul-de-sac (pouch of Douglas) ──
d.add(Polygon([cx-5, 105, cx+5, 95, cx+30, 110, cx+20, 125, cx-5, 120],
fillColor=colors.HexColor("#AED6F1"), strokeColor=MID_BLUE, strokeWidth=1.2))
d.add(String(cx+5, 107, "Post. Cul-de-Sac", fontSize=6.8, fillColor=DARK_BLUE, fontName="Helvetica-Bold"))
# ── Uterus body ──
d.add(Polygon([cx-45,85, cx-10,140, cx+10,140, cx+45,85, cx+20,65, cx-20,65],
fillColor=colors.HexColor("#FADBD8"), strokeColor=colors.HexColor("#922B21"), strokeWidth=1.5))
d.add(String(cx-18, 100, "Uterus", fontSize=8, fillColor=ACCENT, fontName="Helvetica-Bold"))
# ── Cervix ──
d.add(Rect(cx-16, 42, 32, 30, rx=5, ry=5,
fillColor=colors.HexColor("#F5B7B1"), strokeColor=ACCENT, strokeWidth=1.5))
d.add(String(cx-12, 53, "Cervix", fontSize=7.5, fillColor=ACCENT, fontName="Helvetica-Bold"))
# ── Vaginal canal ──
d.add(Rect(cx-14, 10, 28, 32, rx=4, ry=4,
fillColor=colors.HexColor("#FDECEA"), strokeColor=ACCENT, strokeWidth=1))
d.add(String(cx-12, 22, "Vagina", fontSize=7, fillColor=TEXT_DARK, fontName="Helvetica"))
# ── Bladder ──
d.add(Polygon([cx-80, 55, cx-55, 80, cx-20, 80, cx-18, 50, cx-55, 40],
fillColor=colors.HexColor("#D1F2EB"), strokeColor=GREEN, strokeWidth=1.5))
d.add(String(cx-75, 58, "Bladder", fontSize=7.5, fillColor=GREEN, fontName="Helvetica-Bold"))
# ── Vesicouterine space (surgical plane) ──
d.add(Line(cx-18, 75, cx-55, 78, strokeColor=GREEN, strokeWidth=2))
d.add(String(cx-100, 82, "Vesicouterine", fontSize=6.5, fillColor=GREEN, fontName="Helvetica-Bold"))
d.add(String(cx-100, 74, "Space", fontSize=6.5, fillColor=GREEN, fontName="Helvetica-Bold"))
# ── Uterosacral ligament (left) ──
d.add(Line(cx+10, 72, cx+30, 108, strokeColor=GOLD, strokeWidth=2))
d.add(String(cx+32, 108, "USL", fontSize=7, fillColor=GOLD, fontName="Helvetica-Bold"))
# ── Cardinal ligament (right label) ──
d.add(Line(cx+44, 85, cx+65, 100, strokeColor=colors.HexColor("#8E44AD"), strokeWidth=2))
d.add(String(cx+67, 98, "Cardinal Lig.", fontSize=6.5,
fillColor=colors.HexColor("#8E44AD"), fontName="Helvetica-Bold"))
# ── Uterine artery ──
d.add(Line(cx-45, 90, cx-70, 108, strokeColor=ACCENT, strokeWidth=1.8))
d.add(String(cx-108, 110, "Uterine A.", fontSize=6.5, fillColor=ACCENT, fontName="Helvetica-Bold"))
# ── Anterior peritoneal fold ──
d.add(Line(cx-18, 82, cx-52, 82, strokeColor=MID_BLUE, strokeWidth=1.5, strokeDashArray=[3,2]))
d.add(String(cx-115, 84, "Ant. Peritoneal", fontSize=6.5, fillColor=MID_BLUE, fontName="Helvetica-Bold"))
d.add(String(cx-115, 76, "Fold", fontSize=6.5, fillColor=MID_BLUE, fontName="Helvetica-Bold"))
# ── Colpotomy line ──
d.add(Line(cx-14, 42, cx+16, 42, strokeColor=colors.HexColor("#F39C12"), strokeWidth=2.5))
d.add(String(cx-14, 32, "Colpotomy Line", fontSize=7, fillColor=colors.HexColor("#F39C12"),
fontName="Helvetica-Bold"))
# Legend
legend_items = [
(ACCENT, "Uterus / Cervix"),
(GREEN, "Bladder / Vesicouterine Plane"),
(GOLD, "Uterosacral Lig. (USL)"),
(colors.HexColor("#8E44AD"), "Cardinal Lig."),
(MID_BLUE, "Peritoneal Fold (dashed)"),
(colors.HexColor("#F39C12"), "Colpotomy Line"),
]
lx, ly = 8, 12
for i, (col, label) in enumerate(legend_items):
d.add(Rect(lx, ly + i*14, 10, 8, fillColor=col, strokeColor=None))
d.add(String(lx+14, ly + i*14 + 1, label, fontSize=6.5, fillColor=TEXT_DARK, fontName="Helvetica"))
return d
# ─── Page header/footer callback ───────────────────────────────────────────────
def on_page(canvas, doc):
canvas.saveState()
# Header bar
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, H - 1.4*cm, W, 1.4*cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawString(1.8*cm, H - 0.9*cm, "VAGINAL HYSTERECTOMY — Surgical Planes & Landmarks")
canvas.setFont("Helvetica", 8)
canvas.drawRightString(W - 1.8*cm, H - 0.9*cm, "Quick Reference Guide")
# Footer
canvas.setFillColor(GREY_MID)
canvas.rect(0, 0, W, 1.1*cm, fill=1, stroke=0)
canvas.setFillColor(colors.HexColor("#555555"))
canvas.setFont("Helvetica-Oblique", 7.5)
canvas.drawString(1.8*cm, 0.42*cm, "For surgical education. Not a substitute for clinical training.")
canvas.drawRightString(W - 1.8*cm, 0.42*cm, f"Page {doc.page}")
canvas.restoreState()
# ─── Build content ─────────────────────────────────────────────────────────────
story = []
# ══════════════════════════════════════════════════════════════════════
# COVER BLOCK
# ══════════════════════════════════════════════════════════════════════
class CoverBlock(Flowable):
def __init__(self, width, height=110):
Flowable.__init__(self)
self.width = width
self.height = height
def draw(self):
c = self.canv
c.setFillColor(DARK_BLUE)
c.roundRect(0, 0, self.width, self.height, 8, fill=1, stroke=0)
c.setFillColor(MID_BLUE)
c.roundRect(4, 4, self.width-8, self.height-8, 6, fill=0, stroke=1)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 20)
c.drawCentredString(self.width/2, self.height-38, "Vaginal Hysterectomy")
c.setFont("Helvetica-Bold", 14)
c.drawCentredString(self.width/2, self.height-58, "Surgical Planes & Landmarks")
c.setFillColor(LIGHT_BLUE)
c.setFont("Helvetica", 10)
c.drawCentredString(self.width/2, self.height-78, "Quick-Reference Guide for Surgical Trainees")
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 8)
c.drawCentredString(self.width/2, 14, "Berek & Novak's Gynecology | Campbell-Walsh-Wein Urology")
def wrap(self, w, h): return self.width, self.height
story.append(CoverBlock(CONTENT_W))
story.append(Spacer(1, 10))
# ─ intro note
story += body(
"This guide summarises the key surgical planes, anatomical landmarks, and ligament complexes "
"encountered during vaginal hysterectomy, in operative sequence. Mastery of these planes is "
"essential for safe dissection, haemostasis, and avoidance of urinary tract and bowel injury."
)
story += sp(4)
# ══════════════════════════════════════════════════════════════════════
# SECTION 1 — SURGICAL PLANES OVERVIEW (DIAGRAM)
# ══════════════════════════════════════════════════════════════════════
story += section_header("Surgical Planes — Schematic Overview", DARK_BLUE, "1")
story.append(make_anatomy_diagram())
story += [Spacer(1, 4)]
story += [Paragraph(
"Sagittal schematic of female pelvic anatomy showing key surgical planes and structures encountered "
"during vaginal hysterectomy. Colpotomy line (orange) = site of circumferential vaginal incision.",
sCaption)]
story += sp(6)
# ══════════════════════════════════════════════════════════════════════
# SECTION 2 — STEP-BY-STEP PLANES TABLE
# ══════════════════════════════════════════════════════════════════════
story += section_header("Operative Sequence — Planes & Key Actions", MID_BLUE, "2")
steps_data = [
[Paragraph("Step", sTableHdr), Paragraph("Plane / Space", sTableHdr),
Paragraph("Key Landmarks", sTableHdr), Paragraph("Critical Points", sTableHdr)],
[Paragraph("1\nColpotomy", sTableCellBold),
Paragraph("Vaginal epithelium\n→ cervical stroma", sTableCell),
Paragraph("Cervicovaginal junction\nBladder sulcus\nExternal cervical os", sTableCell),
Paragraph("Incise ≥1 cm distal to bladder. Inject dilute epi for hydrodissection. "
"Angle posteriorly to facilitate cul-de-sac entry.", sTableCell)],
[Paragraph("2\nPost. Cul-de-Sac\nEntry", sTableCellBold),
Paragraph("Pouch of Douglas\n(rectouterine space)", sTableCell),
Paragraph("Posterior peritoneal fold\nUterosacral ligaments\nCul-de-sac of Douglas", sTableCell),
Paragraph("Peritoneum glistens — confirm entry with finger. Secure to vaginal wall with "
"figure-of-eight suture. Inspect for adhesive disease.", sTableCell)],
[Paragraph("3\nUSL Ligation", sTableCellBold),
Paragraph("Parametrium\n(inferior portion)", sTableCell),
Paragraph("Uterosacral ligament\nLower cardinal ligament\nUreter (lateral — must stay away)", sTableCell),
Paragraph("Clamp perpendicular to uterine axis, tips touch cervix. Keep pedicle <0.5 cm distal "
"to clamp. Tag suture (most inferior pedicle).", sTableCell)],
[Paragraph("4\nVesicouterine\nSpace", sTableCellBold),
Paragraph("Vesicouterine space\n→ Anterior peritoneum", sTableCell),
Paragraph("Anterior peritoneal fold\nVesicocervical fascia\nBladder base", sTableCell),
Paragraph("Scissors directed toward uterus. Correct plane is avascular. Once peritoneum "
"identified: elevate, enter sharply. Place Heaney retractor to protect bladder.", sTableCell)],
[Paragraph("5\nCardinal Lig.\nLigation", sTableCellBold),
Paragraph("Parametrium\n(lateral cervix)", sTableCell),
Paragraph("Cardinal (Mackenrodt's) ligament\nUterine vasculature\nUreter (runs 1.5 cm lateral)", sTableCell),
Paragraph("Sequential bites medial to ureter. Ligate individually or with USL. Continue "
"superiorly until uterine arteries are reached.", sTableCell)],
[Paragraph("6\nUterine Artery\nLigation", sTableCellBold),
Paragraph("At level of internal os\n(uterine isthmus)", sTableCell),
Paragraph("Uterine artery & vein\nBroad ligament\nUreter (crosses under — 'water under the bridge')", sTableCell),
Paragraph("Double ligate: suture-tie + medial transfixion ligature. Ureter at greatest "
"risk here — runs 1–2 cm laterally. Confirm both sides before fundal delivery.", sTableCell)],
[Paragraph("7\nFundal\nDelivery", sTableCellBold),
Paragraph("Broad ligament\nMesosalpinx", sTableCell),
Paragraph("Broad ligament\nRound ligament\nUtero-ovarian ligament\nFallopian tube", sTableCell),
Paragraph("Deliver fundus posteriorly. Finger guides clamp behind utero-ovarian pedicle. "
"Double ligate upper pedicles. Tag sutures for identification.", sTableCell)],
[Paragraph("8\nAdnexal\nRemoval\n(if performed)", sTableCellBold),
Paragraph("Infundibulopelvic\nligament space", sTableCell),
Paragraph("Infundibulopelvic ligament\nOvarian vessels\nUreter (nearby — confirm first)", sTableCell),
Paragraph("Draw ovary medially with Babcock. Clamp across IP ligament. Transfixion tie + "
"suture ligature. If mesosalpinx delicate, take serial bites.", sTableCell)],
[Paragraph("9\nVault\nClosure", sTableCellBold),
Paragraph("Vaginal cuff\n(peritonealised)", sTableCell),
Paragraph("Vaginal cuff angles\nUSL stumps\nCut edges of peritoneum", sTableCell),
Paragraph("Incorporate USL stumps into lateral angles for apical support. Close with "
"delayed absorbable suture. Inspect all pedicles for haemostasis.", sTableCell)],
]
col_w = [2.0*cm, 3.0*cm, 4.5*cm, 7.3*cm]
steps_table = Table(steps_data, colWidths=col_w, repeatRows=1)
steps_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, GREY_LIGHT]),
("BACKGROUND", (0,1), (0,-1), LIGHT_BLUE),
("GRID", (0,0), (-1,-1), 0.4, GREY_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(steps_table)
story += sp(8)
# ══════════════════════════════════════════════════════════════════════
# SECTION 3 — LIGAMENT ANATOMY REFERENCE
# ══════════════════════════════════════════════════════════════════════
story += section_header("Ligament & Support Structure Reference", DARK_BLUE, "3")
lig_data = [
[Paragraph("Structure", sTableHdr), Paragraph("Attachments", sTableHdr),
Paragraph("Surgical Significance", sTableHdr), Paragraph("Suture", sTableHdr)],
[Paragraph("Uterosacral Ligament (USL)", sTableCellBold),
Paragraph("Cervix/upper vagina → sacral fascia S2–S4", sTableCell),
Paragraph("Primary apical support. First pedicle clamped. Tag for vault suspension / McCall culdoplasty.", sTableCell),
Paragraph("0-Vicryl / 0-PGA transfixion", sTableCell)],
[Paragraph("Cardinal Ligament\n(Mackenrodt's)", sTableCellBold),
Paragraph("Cervix & upper vagina → pelvic sidewall / internal iliac fascia", sTableCell),
Paragraph("Contains uterine vessels. Ureter runs 1.5 cm lateral. Sequential bites after USL.", sTableCell),
Paragraph("0-Vicryl transfixion", sTableCell)],
[Paragraph("Uterine Artery", sTableCellBold),
Paragraph("Arises from anterior division of internal iliac artery", sTableCell),
Paragraph("Ureter crosses beneath it laterally ('water under the bridge'). Double ligate before fundal delivery.", sTableCell),
Paragraph("Suture-tie + transfixion\n0-Vicryl", sTableCell)],
[Paragraph("Utero-ovarian Ligament\n+ Round Ligament", sTableCellBold),
Paragraph("Ovary/cornu of uterus → broad ligament / inguinal canal", sTableCell),
Paragraph("Upper pedicle — clamped last. Finger guides clamp posteriorly. Double ligate.", sTableCell),
Paragraph("0-Vicryl double ligature", sTableCell)],
[Paragraph("Infundibulopelvic\nLigament (IP / Suspensory)", sTableCellBold),
Paragraph("Ovary → pelvic sidewall; contains ovarian vessels", sTableCell),
Paragraph("Only clamped if adnexal removal planned. Ureter runs parallel below — confirm first.", sTableCell),
Paragraph("Transfixion + ligature\n0-Vicryl", sTableCell)],
[Paragraph("Broad Ligament", sTableCellBold),
Paragraph("Peritoneal fold enclosing uterus, tubes, ovaries", sTableCell),
Paragraph("Contains parametrial tissue and vasculature. Opened during upper pedicle dissection.", sTableCell),
Paragraph("Haemostasis as needed", sTableCell)],
]
lig_col_w = [3.8*cm, 4.0*cm, 5.8*cm, 3.2*cm]
lig_table = Table(lig_data, colWidths=lig_col_w, repeatRows=1)
lig_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MID_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, PALE_GREEN]),
("GRID", (0,0), (-1,-1), 0.4, GREY_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(lig_table)
story += sp(8)
# ══════════════════════════════════════════════════════════════════════
# SECTION 4 — DANGER ZONES & INJURY AVOIDANCE
# ══════════════════════════════════════════════════════════════════════
story += section_header("Danger Zones & Injury Avoidance", ACCENT, "4")
danger_data = [
[Paragraph("Structure at Risk", sTableHdr), Paragraph("When at Risk", sTableHdr),
Paragraph("Prevention Strategy", sTableHdr)],
[Paragraph("Ureter", sTableCellBold),
Paragraph("Cardinal lig. ligation\nUterine artery ligation\nIP ligament ligation", sTableCell),
Paragraph("Keep clamps medial. Ureter runs 1–2 cm lateral to cervix and crosses beneath uterine artery. "
"Confirm peristalsis on both ureters at end of case if concern.", sTableCell)],
[Paragraph("Bladder", sTableCellBold),
Paragraph("Anterior colpotomy\nVesicouterine space dissection\nVault closure", sTableCell),
Paragraph("Incise ≥1 cm distal to bladder sulcus. Use Heaney retractor to protect bladder throughout anterior dissection. "
"Backfill if uncertain. If cystotomy: complete hysterectomy then repair in 2 layers.", sTableCell)],
[Paragraph("Rectum", sTableCellBold),
Paragraph("Posterior colpotomy\nCul-de-sac entry", sTableCell),
Paragraph("Place traction on posterior vaginal epithelium, not deep tissues. Confirm entry into peritoneal cavity "
"digitally before extending incision.", sTableCell)],
[Paragraph("Uterine Vessels\n(haemorrhage)", sTableCellBold),
Paragraph("Uterine artery step\nCardinal lig. ligation\nUpper pedicle delivery", sTableCell),
Paragraph("Double ligate uterine artery (suture-tie + transfixion). Keep pedicles <0.5 cm distal to clamp. "
"Tag all pedicles with hemostats for identification at haemostasis check.", sTableCell)],
[Paragraph("Pelvic Splanchnic\nNerves / Hypogastric\nPlexus", sTableCellBold),
Paragraph("Deep parametrial dissection\nUSL ligation (posterior)", sTableCell),
Paragraph("Avoid excessively deep or lateral bites in the posterior parametrium. Nerve-sparing technique "
"relevant in radical procedures. Injury may cause bladder/bowel/sexual dysfunction.", sTableCell)],
]
danger_col_w = [3.5*cm, 4.5*cm, 8.8*cm]
danger_table = Table(danger_data, colWidths=danger_col_w, repeatRows=1)
danger_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), ACCENT),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, PALE_RED]),
("GRID", (0,0), (-1,-1), 0.4, GREY_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(danger_table)
story += sp(8)
# ══════════════════════════════════════════════════════════════════════
# SECTION 5 — SURGICAL PLANES QUICK-REFERENCE BOX
# ══════════════════════════════════════════════════════════════════════
story += section_header("Key Surgical Planes at a Glance", MID_BLUE, "5")
planes_data = [
[Paragraph("Plane", sTableHdr), Paragraph("Boundaries", sTableHdr),
Paragraph("How to Enter", sTableHdr), Paragraph("Tissue Character", sTableHdr)],
[Paragraph("Posterior\nCul-de-Sac", sTableCellBold),
Paragraph("Posterior uterus / cervix anteriorly; rectum posteriorly; peritoneum superiorly", sTableCell),
Paragraph("Sharply after placing traction on posterior vaginal epithelium; peritoneum glistens and tents", sTableCell),
Paragraph("Avascular peritoneal sheen; feel of popping into peritoneal cavity", sTableCell)],
[Paragraph("Vesicouterine\nSpace", sTableCellBold),
Paragraph("Bladder posteriorly; anterior uterus/cervix anteriorly; vesicouterine peritoneum superiorly", sTableCell),
Paragraph("Mayo scissors directed toward uterus; blunt finger once correct plane found", sTableCell),
Paragraph("Avascular, loose areolar tissue; correct plane is bloodless; resistance = wrong plane", sTableCell)],
[Paragraph("Parametrial\nSpace", sTableCellBold),
Paragraph("Lateral to cervix and uterus; medial to broad ligament/pelvic sidewall", sTableCell),
Paragraph("Sequential clamping of USL, cardinal, and uterine vessel pedicles progressing superiorly", sTableCell),
Paragraph("Fibrovascular — contains uterine vessels; bleeding expected; keep clamps medial", sTableCell)],
[Paragraph("Retroperitoneal\n(Adnexal)", sTableCellBold),
Paragraph("Above broad ligament; lateral to round ligament; contains IP ligament and ovarian vessels", sTableCell),
Paragraph("Open broad ligament after uterine delivery; identify IP ligament under direct vision", sTableCell),
Paragraph("Fatty/retroperitoneal; ureter runs parallel below IP ligament — confirm position", sTableCell)],
]
planes_col_w = [2.8*cm, 4.8*cm, 4.8*cm, 4.4*cm]
planes_table = Table(planes_data, colWidths=planes_col_w, repeatRows=1)
planes_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MID_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
("GRID", (0,0), (-1,-1), 0.4, GREY_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(planes_table)
story += sp(8)
# ══════════════════════════════════════════════════════════════════════
# SECTION 6 — MNEMONIC & TIPS BOX
# ══════════════════════════════════════════════════════════════════════
story += section_header("Operative Mnemonics & Pearls", GREEN, "6")
# Two-column tips layout
col1 = [
Paragraph("<b>Sequence Mnemonic — \"Post Cardinal Vesical Uterine Fundal\"</b>", sBody),
Paragraph("• <b>Post</b>erior cul-de-sac entry", sBullet),
Paragraph("• <b>Cardinal</b>/uterosacral ligation", sBullet),
Paragraph("• <b>Vesical</b> space — anterior entry", sBullet),
Paragraph("• <b>Uterine</b> artery ligation", sBullet),
Paragraph("• <b>Fundal</b> delivery + upper pedicles", sBullet),
Spacer(1,6),
Paragraph("<b>Ureter Rule of Thumb</b>", sH3),
Paragraph("• 2 cm lateral to cervix at the level of the internal os", sBullet),
Paragraph("• Crosses under uterine artery: <i>\"water under the bridge\"</i>", sBullet),
Paragraph("• Confirm bilateral peristalsis at case end if concern", sBullet),
]
col2 = [
Paragraph("<b>Avascular Plane Clues</b>", sBody),
Paragraph("• Correct vesicouterine plane = bloodless, white, loose tissue", sBullet),
Paragraph("• Resistance to blunt dissection = wrong plane (stop, go sharply)", sBullet),
Paragraph("• Peritoneal entry: glistening surface + 'pop' sensation", sBullet),
Spacer(1,6),
Paragraph("<b>Haemostasis Checklist</b>", sH3),
Paragraph("• Tag all pedicles (most inferior = USL, most superior = UO ligament)", sBullet),
Paragraph("• Inspect in sequence before vault closure", sBullet),
Paragraph("• Pedicle distal to clamp ≤ 0.5 cm", sBullet),
Paragraph("• Flash second clamp if large pedicle during ligation", sBullet),
]
tips_table = Table([[col1, col2]], colWidths=[CONTENT_W/2 - 3, CONTENT_W/2 - 3])
tips_table.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("BACKGROUND", (0,0), (0,0), PALE_GREEN),
("BACKGROUND", (1,0), (1,0), PALE_AMBER),
("BOX", (0,0), (0,0), 0.5, GREEN),
("BOX", (1,0), (1,0), 0.5, GOLD),
("ROUNDEDCORNERS", (0,0), (-1,-1), [4,4,4,4]),
]))
story.append(tips_table)
story += sp(8)
# ══════════════════════════════════════════════════════════════════════
# SECTION 7 — MCCALL CULDOPLASTY NOTE
# ══════════════════════════════════════════════════════════════════════
story += section_header("Vault Suspension: McCall Culdoplasty", DARK_BLUE, "7")
story += body(
"At vault closure, apical support is restored by incorporating the uterosacral ligament stumps "
"into the cuff repair. The McCall culdoplasty additionally plicates the peritoneum of the "
"posterior cul-de-sac to prevent enterocele formation."
)
story += sp(3)
mccall_data = [
[Paragraph("Step", sTableHdr), Paragraph("Action", sTableHdr)],
[Paragraph("1", sTableCellBold),
Paragraph("Identify the tagged uterosacral ligament stumps bilaterally (most inferior pedicles).", sTableCell)],
[Paragraph("2", sTableCellBold),
Paragraph("Place a delayed absorbable suture through the posterior peritoneum, across the "
"cul-de-sac, and through both uterosacral stumps.", sTableCell)],
[Paragraph("3", sTableCellBold),
Paragraph("Pass the same suture (or a separate one) through the posterior vaginal cuff epithelium "
"bilaterally to incorporate the stumps into the cuff.", sTableCell)],
[Paragraph("4", sTableCellBold),
Paragraph("Tie the suture to plicate the posterior cul-de-sac peritoneum and suspend the vaginal apex "
"to the uterosacral ligaments.", sTableCell)],
[Paragraph("5", sTableCellBold),
Paragraph("Close the vaginal cuff with interrupted or running 0-gauge delayed absorbable suture, "
"incorporating the lateral uterosacral stumps in the angles for maximum apical support.", sTableCell)],
]
mc_col_w = [1.5*cm, CONTENT_W - 1.5*cm]
mc_table = Table(mccall_data, colWidths=mc_col_w, repeatRows=1)
mc_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ALIGN", (0,0), (0,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
("GRID", (0,0), (-1,-1), 0.4, GREY_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
]))
story.append(mc_table)
story += sp(8)
# ══════════════════════════════════════════════════════════════════════
# REFERENCES
# ══════════════════════════════════════════════════════════════════════
story += hline()
story += sp(4)
story.append(Paragraph("<b>References</b>", sH2))
refs = [
"Berek JS (ed.). <i>Berek & Novak's Gynecology</i>, 16th ed. Wolters Kluwer, 2020. Chapter 27: Hysterectomy.",
"Wein AJ et al. (eds.). <i>Campbell-Walsh-Wein Urology</i>, 12th ed. Elsevier, 2021. "
"Chapter 68: Vaginal Hysterectomy with McCall Culdoplasty Technique.",
"Townsend CM et al. (eds.). <i>Sabiston Textbook of Surgery</i>, 21st ed. Elsevier, 2022. "
"Section: Vaginal Hysterectomy.",
]
for r in refs:
story.append(Paragraph(f"• {r}", sBullet))
story += sp(6)
story.append(Paragraph(
"This guide is intended for surgical education and training purposes only. "
"Clinical decisions must be based on complete operative assessment, institutional protocols, and consultant guidance.",
sFooter))
# ─── Build PDF ────────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"PDF generated: {OUTPUT}")