Expansive detailed knowledge on ante-partum fetal monitoring
antepartum fetal surveillance monitoring
| Result | Definition | Clinical Action |
|---|---|---|
| Negative (Normal) | No late or significant variable decelerations | Reassuring; repeat in 1 week |
| Positive (Abnormal) | Late decelerations following ≥50% of contractions | High-risk; delivery planning or intensive monitoring |
| Equivocal-Suspicious | Intermittent late or significant variable decelerations | Repeat test |
| Equivocal-Hyperstimulation | Decelerations with contractions >90 sec or frequency >2 in 10 min | Repeat when uterine activity normalizes |
| Unsatisfactory | <3 contractions in 10 minutes or uninterpretable tracing | Repeat test |
| Variable | Normal (Score 2) | Abnormal (Score 0) |
|---|---|---|
| Fetal Breathing Movement (FBM) | ≥30 seconds of sustained breathing in 30 minutes | <30 seconds in 30 minutes |
| Gross Body Movement (FM) | ≥3 separate limb/trunk movements in 30 minutes | ≤2 movements in 30 minutes |
| Fetal Tone (FT) | ≥1 episode of extension followed by return to flexion (limb or spine) | Semi- or full extension without return; absent movement counts as absent tone |
| FHR Reactivity (NST) | ≥2 accelerations of ≥15 bpm × ≥15 sec with movement | <2 qualifying accelerations during observation |
| Amniotic Fluid Volume (AFV) | A pocket ≥2 cm in two perpendicular planes (or AFI ≥5 cm) | Largest pocket <2 cm in two perpendicular planes |
| BPP Score | Risk of Fetal Acidosis | Management |
|---|---|---|
| 10/10 or 8/10 (normal AFV) | Very low (<1 in 1000) | Reassured; routine follow-up |
| 8/10 (abnormal AFV) | Moderate | Deliver if ≥36 wks; intensive monitoring if preterm |
| 6/10 | Equivocal (~variable) | Deliver if ≥36 wks or mature; repeat in 4-6 hrs if preterm |
| 4/10 | High | Deliver unless remote prematurity |
| 2/10 or 0/10 | Very high | Deliver regardless of gestational age |

| Finding | Significance |
|---|---|
| Elevated S/D ratio / high PI/RI | Increased placental resistance; ~30% of villous tree affected |
| Absent End-Diastolic Velocity (AEDV) | ~60-70% of villous tree obliterated; high risk of stillbirth and neonatal complications |
| Reversed End-Diastolic Velocity (REDV) | >70% obliteration; very high risk; imminent fetal decompensation |

Weekly or twice-weekly modified BPP (NST + AFI)
|
Reactive NST + normal AFI
|
Reassuring - repeat per schedule
|
Non-reactive NST or abnormal AFI
|
V
Full BPP (ultrasound 30 min)
|
BPP ≥8/10, normal AFV → Continue monitoring
BPP 6/10 → Repeat in 4-6 hrs; if still 6, consider delivery
BPP ≤4/10 → Deliver
|
Concurrent UA Doppler (FGR cases)
AEDV/REDV → Escalate to daily monitoring + consider DV Doppler
Reversed DV a-wave → Delivery (with corticosteroids if <34 wks)
| Test | False-Negative Rate | False-Positive Rate | Key Limitations |
|---|---|---|---|
| NST alone | ~1.9/1000 (weekly) | ~80% of non-reactive | Sleep states, medications, prematurity |
| CST | ~0.04% | ~30% | Contraindications, inconvenience |
| Full BPP | ~0.7/1000 | Lower than NST alone | Time-consuming, operator-dependent |
| Modified BPP | ~0.8/1000 | Intermediate | Two-variable only |
| UA Doppler | Low in FGR | Moderate in low-risk | Requires expertise; not useful in all conditions |
| Method | What It Measures | Acute or Chronic | When Used |
|---|---|---|---|
| Kick counts | Gross fetal movement | Acute | Daily, all at-risk patients |
| NST | FHR reactivity (CNS-CVS coupling) | Acute | Weekly or twice-weekly |
| CST/OCT | Uteroplacental reserve | Acute | Adjunct when NST non-reactive |
| Full BPP | 5 biophysical variables | Acute + Chronic | When NST non-reactive; FGR |
| Modified BPP | NST + AFI | Acute + Chronic | First-line outpatient surveillance |
| UA Doppler | Placental vascular resistance | Intermediate | FGR, hypertension |
| MCA Doppler PSV | Fetal hematocrit/anemia | Acute | Isoimmunization, hydrops |
| MCA PI/CPR | Brain sparing / redistribution | Intermediate | FGR, placental insufficiency |
| DV Doppler | Venous/cardiac function | Late/severe | Late-stage FGR; timing delivery |
| Uterine artery Doppler | Maternal spiral artery remodeling | Chronic/predictive | 20-24 week screening |
| AFI/SDP | Amniotic fluid (cumulative renal perfusion) | Chronic | Part of BPP; oligohydramnios workup |
Build all this into a pdf
mkdir -p /tmp/workspace/antepartum-fetal-monitoring && ls /tmp/workspace/antepartum-fetal-monitoring/
~/antepartum-fetal-monitoring/build_pdf.py
"""
Ante-partum Fetal Monitoring - Comprehensive PDF Generator
Using ReportLab for rich formatting with tables, headings, and images.
"""
import urllib.request
import io
import os
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether, Image
)
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
OUTPUT = "/tmp/workspace/antepartum-fetal-monitoring/Antepartum_Fetal_Monitoring.pdf"
# ── Colour palette ─────────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1a3a5c")
TEAL = colors.HexColor("#0d7377")
LIGHT_TEAL= colors.HexColor("#e8f5f5")
ACCENT = colors.HexColor("#c0392b")
MID_GREY = colors.HexColor("#4a4a4a")
LIGHT_GREY= colors.HexColor("#f5f5f5")
TABLE_HDR = colors.HexColor("#1a3a5c")
TABLE_ALT = colors.HexColor("#eaf4f4")
WHITE = colors.white
BLACK = colors.black
# ── Styles ─────────────────────────────────────────────────────────────────────
BASE = getSampleStyleSheet()
def make_styles():
styles = {}
styles["cover_title"] = ParagraphStyle(
"cover_title", fontSize=28, fontName="Helvetica-Bold",
textColor=WHITE, alignment=TA_CENTER, leading=34, spaceAfter=8
)
styles["cover_sub"] = ParagraphStyle(
"cover_sub", fontSize=14, fontName="Helvetica",
textColor=colors.HexColor("#d0eaee"), alignment=TA_CENTER, leading=18
)
styles["cover_meta"] = ParagraphStyle(
"cover_meta", fontSize=10, fontName="Helvetica",
textColor=colors.HexColor("#a0c8d0"), alignment=TA_CENTER
)
styles["h1"] = ParagraphStyle(
"h1", fontSize=18, fontName="Helvetica-Bold",
textColor=WHITE, leading=22, spaceBefore=0, spaceAfter=0,
leftIndent=0, borderPad=0
)
styles["h2"] = ParagraphStyle(
"h2", fontSize=13, fontName="Helvetica-Bold",
textColor=NAVY, leading=18, spaceBefore=16, spaceAfter=4,
borderPad=2
)
styles["h3"] = ParagraphStyle(
"h3", fontSize=11, fontName="Helvetica-Bold",
textColor=TEAL, leading=15, spaceBefore=10, spaceAfter=3
)
styles["h4"] = ParagraphStyle(
"h4", fontSize=10, fontName="Helvetica-BoldOblique",
textColor=MID_GREY, leading=14, spaceBefore=8, spaceAfter=2
)
styles["body"] = ParagraphStyle(
"body", fontSize=9.5, fontName="Helvetica",
textColor=MID_GREY, leading=14, spaceBefore=3, spaceAfter=3,
alignment=TA_JUSTIFY
)
styles["bullet"] = ParagraphStyle(
"bullet", fontSize=9.5, fontName="Helvetica",
textColor=MID_GREY, leading=14, spaceBefore=2, spaceAfter=2,
leftIndent=14, bulletIndent=4, alignment=TA_LEFT
)
styles["sub_bullet"] = ParagraphStyle(
"sub_bullet", fontSize=9, fontName="Helvetica",
textColor=MID_GREY, leading=13, spaceBefore=1, spaceAfter=1,
leftIndent=28, bulletIndent=18
)
styles["caption"] = ParagraphStyle(
"caption", fontSize=8.5, fontName="Helvetica-Oblique",
textColor=colors.HexColor("#666666"), leading=12,
spaceBefore=4, spaceAfter=8, alignment=TA_CENTER
)
styles["note_box"] = ParagraphStyle(
"note_box", fontSize=9, fontName="Helvetica",
textColor=colors.HexColor("#1a3a5c"), leading=13,
spaceBefore=2, spaceAfter=2, leftIndent=6
)
styles["source"] = ParagraphStyle(
"source", fontSize=7.5, fontName="Helvetica-Oblique",
textColor=colors.HexColor("#888888"), leading=11,
spaceBefore=2, spaceAfter=2
)
return styles
S = make_styles()
# ── Helpers ────────────────────────────────────────────────────────────────────
def section_header(number, title):
"""Returns a navy banner row for a major section."""
data = [[Paragraph(f"{number}. {title}", S["h1"])]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
("ROUNDEDCORNERS", [4,4,4,4]),
]))
return t
def h2(text): return Paragraph(text, S["h2"])
def h3(text): return Paragraph(text, S["h3"])
def h4(text): return Paragraph(text, S["h4"])
def p(text): return Paragraph(text, S["body"])
def sp(n=6): return Spacer(1, n)
def hr(): return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#cccccc"), spaceAfter=4, spaceBefore=4)
def bullets(items, style="bullet"):
return [Paragraph(f"• {item}", S[style]) for item in items]
def sub_bullets(items):
return [Paragraph(f" ◦ {item}", S["sub_bullet"]) for item in items]
def note_box(lines):
"""A teal-background callout box."""
content = [Paragraph(l, S["note_box"]) for l in lines]
t = Table([[content]], colWidths=[17*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_TEAL),
("BOX", (0,0), (-1,-1), 1, TEAL),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
]))
return t
def make_table(header_row, data_rows, col_widths, alternate=True):
all_rows = [header_row] + data_rows
t = Table(all_rows, colWidths=col_widths, repeatRows=1)
style_cmds = [
("BACKGROUND", (0,0), (-1,0), TABLE_HDR),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 9),
("ALIGN", (0,0), (-1,0), "CENTER"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 8.5),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#b0c8c8")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, TABLE_ALT] if alternate else [WHITE]),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
]
t.setStyle(TableStyle(style_cmds))
return t
def fetch_image_to_file(url, filename):
"""Download image URL to local file; return path or None."""
try:
path = f"/tmp/workspace/antepartum-fetal-monitoring/{filename}"
urllib.request.urlretrieve(url, path)
return path
except Exception as e:
print(f" [warn] Could not fetch {url}: {e}")
return None
def embed_image(path, width_cm=13, caption=None):
"""Return list of flowables for an image + optional caption."""
if path is None:
return []
try:
img = Image(path, width=width_cm*cm, kind="proportional")
items = [sp(4), img]
if caption:
items.append(Paragraph(caption, S["caption"]))
items.append(sp(4))
return items
except Exception as e:
print(f" [warn] Could not embed image {path}: {e}")
return []
# ── Page template callbacks ────────────────────────────────────────────────────
def on_first_page(canvas, doc):
canvas.saveState()
w, h = A4
# Full-page gradient background (navy to teal)
canvas.setFillColor(NAVY)
canvas.rect(0, 0, w, h, fill=1, stroke=0)
canvas.setFillColor(TEAL)
canvas.rect(0, 0, w, h*0.42, fill=1, stroke=0)
# Decorative top stripe
canvas.setFillColor(colors.HexColor("#0a5c6b"))
canvas.rect(0, h-6, w, 6, fill=1, stroke=0)
canvas.restoreState()
def on_later_pages(canvas, doc):
canvas.saveState()
w, h = A4
# Header bar
canvas.setFillColor(NAVY)
canvas.rect(0, h-22, w, 22, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 8)
canvas.setFillColor(WHITE)
canvas.drawString(1.5*cm, h-14, "Ante-partum Fetal Monitoring — Comprehensive Reference")
canvas.drawRightString(w-1.5*cm, h-14, f"Page {doc.page}")
# Footer line
canvas.setStrokeColor(colors.HexColor("#cccccc"))
canvas.setLineWidth(0.5)
canvas.line(1.5*cm, 1.5*cm, w-1.5*cm, 1.5*cm)
canvas.setFont("Helvetica", 7)
canvas.setFillColor(colors.HexColor("#999999"))
canvas.drawCentredString(w/2, 0.9*cm, "Sources: Creasy & Resnik's Maternal-Fetal Medicine | Pfenninger & Fowler's Procedures for Primary Care | Cochrane Reviews")
canvas.restoreState()
# ── Build document ─────────────────────────────────────────────────────────────
def build():
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.8*cm, bottomMargin=2.5*cm,
title="Ante-partum Fetal Monitoring",
author="Orris Medical Reference",
subject="Obstetrics – Antepartum Surveillance",
)
story = []
# ── Pre-fetch images ──────────────────────────────────────────────────────
print("Fetching images...")
img_bpp = fetch_image_to_file(
"https://cdn.orris.care/cdss_images/ba407a1db3b165de80a1c1caec35548e3164dbe25555ac344cd4bbf8805aead7.png",
"bpp_graph.png"
)
img_mca = fetch_image_to_file(
"https://cdn.orris.care/cdss_images/b198b293f52e244f0d7d1bfe71b2e2f0401aa1f60d05afe57c272bbb74c7676b.png",
"mca_doppler.png"
)
print(f" BPP graph: {img_bpp}")
print(f" MCA Doppler: {img_mca}")
# ══════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════════════════════════════════
story.append(Spacer(1, 5.5*cm))
story.append(Paragraph("ANTE-PARTUM", S["cover_title"]))
story.append(Paragraph("FETAL MONITORING", S["cover_title"]))
story.append(Spacer(1, 0.6*cm))
story.append(HRFlowable(width="60%", thickness=2, color=TEAL, hAlign="CENTER", spaceAfter=12))
story.append(Paragraph("Comprehensive Clinical Reference", S["cover_sub"]))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph("Physiology · Tests · Doppler Velocimetry · Algorithms · Special Situations", S["cover_sub"]))
story.append(Spacer(1, 5.5*cm))
story.append(Paragraph("Sources: Creasy & Resnik's Maternal-Fetal Medicine (9e) · Pfenninger & Fowler's Procedures for Primary Care (3e)", S["cover_meta"]))
story.append(Paragraph("Cochrane Database of Systematic Reviews · FIGO Fetal Growth Initiative 2021", S["cover_meta"]))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("July 2026", S["cover_meta"]))
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 1 — Physiological Basis
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("1", "Physiological Basis of Ante-partum Monitoring"))
story.append(sp(10))
story.append(h2("Core Principle"))
story.append(p(
"Any fetal monitoring technology should be thought of as a <b>screening test for fetal hypoxemia and acidosis</b>, "
"with the ultimate goals of reducing stillbirth and improving neonatal outcome. Fetal assessment assumes that a "
"change in fetal behavior implies a change in fetal status. Maternal evaluation of fetal activity, nonstress "
"testing, and the biophysical profile (BPP) all rely on this principle."
))
story.append(sp())
story.append(h2("Fetal Heart Rate (FHR) Development"))
story.append(p(
"As gestation advances, the FHR is increasingly dominated by the <b>parasympathetic nervous system</b>, "
"resulting in:"
))
story += bullets([
"A gradual decrease in baseline heart rate toward term",
"Increasing beat-to-beat variability",
"Progressive responsiveness to fetal movement (accelerations)",
"Increasing coupling of CNS neurological status to cardiovascular reflex responses",
])
story.append(p(
"The capacity to accelerate the FHR in response to movement is directly linked to fetal oxygenation and "
"metabolic state. Preterm fetuses, growth-restricted fetuses, or those exposed to maternal medications "
"(narcotics, magnesium sulfate) may have attenuated accelerations without true compromise — an important "
"source of false-positive tests."
))
story.append(sp())
story.append(h2("Hierarchy of CNS Vulnerability Under Hypoxia"))
story.append(p(
"Under progressive hypoxia, fetal CNS centres are suppressed in a predictable order from most sensitive "
"to least sensitive. This hierarchy underpins BPP interpretation:"
))
hier_data = [
[Paragraph("<b>Order</b>", S["body"]), Paragraph("<b>Function Lost</b>", S["body"]), Paragraph("<b>Test Affected</b>", S["body"]), Paragraph("<b>CNS Centre</b>", S["body"])],
["1st (most sensitive)", "FHR reactivity", "Non-stress test (NST)", "Cortical / higher centres"],
["2nd", "Fetal breathing movements", "BPP — FBM component", "Respiratory centre"],
["3rd", "Gross body movements", "BPP — FM component", "Motor cortex"],
["4th (least sensitive)", "Fetal tone", "BPP — tone component", "Primitive brainstem reflex"],
["Chronic marker", "Amniotic fluid volume", "BPP — AFV / AFI", "Renal perfusion (cumulative)"],
]
story.append(make_table(hier_data[0], hier_data[1:], [2.5*cm, 4*cm, 4.5*cm, 6*cm]))
story.append(sp(6))
story.append(note_box([
"⚕ Key point: Amniotic fluid volume reflects cumulative placental function over days-to-weeks "
"(analogous to HbA1c for glucose). It is the last variable to normalise and a late sign of chronic insufficiency.",
]))
story.append(sp(8))
story.append(h2("Ideal Characteristics of a Fetal Monitoring Test"))
story.append(p("From Creasy & Resnik's Maternal-Fetal Medicine, an ideal monitoring system should:"))
story += bullets([
"Gather a wide range of information — versatile for all maternal/fetal conditions and gestational ages",
"Detect fetal peril with high sensitivity, specificity, and timeliness to allow preventive intervention",
"Have a <b>low false-positive rate</b>, especially at earlier gestational ages when prematurity risk is highest",
"Have <b>high sensitivity</b> for modest compromise to permit early enough intervention to prevent injury",
"Have a high and durable <b>negative predictive value</b> — reliably excludes stillbirth over a predictable interval",
"Incorporate <b>multiple variables</b> to address the complexity of normal fetal behavior",
"Detect compromise from multiple causes: asphyxia, infection, metabolic abnormalities, anemia",
"Be applicable in outpatient and inpatient settings, use available technology, and be reproducible",
"Demonstrate measurable benefits in reducing perinatal mortality and morbidity in high-risk populations",
])
story.append(p(
"<i>No single test satisfies all these objectives. In balancing the risks of stillbirth from intrauterine "
"decompensation against neonatal morbidity from prematurity, the use of multiple methods of assessment "
"is more likely to yield reliable results.</i>"
))
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 2 — Methods of Surveillance
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("2", "Methods of Antepartum Surveillance"))
story.append(sp(10))
# ── 2.1 Kick Counts
story.append(h2("2.1 Fetal Movement Counting (Kick Counts)"))
story.append(p(
"The simplest and most accessible form of surveillance. The mother records active fetal movements daily. "
"Decreased fetal movement may precede fetal compromise by days and warrants immediate clinical evaluation."
))
story += bullets([
"<b>Cardiff 'Count to Ten' method:</b> Count movements until 10 are felt; normal if 10 occur within 2 hours",
"<b>Sadovsky method:</b> Count for 1 hour three times daily; normal is >4 movements per hour",
"<b>Absence of movement for >12 hours:</b> warrants immediate assessment",
])
story.append(p("Limitations: subjective, affected by maternal perception, sleep states, oligohydramnios, anterior placenta, maternal obesity."))
story.append(sp(8))
# ── 2.2 NST
story.append(h2("2.2 Non-Stress Test (NST)"))
story.append(p(
"The NST is the cornerstone of outpatient antepartum surveillance. It assesses FHR reactivity as a "
"surrogate for intact fetal CNS-cardiovascular coupling — the most sensitive short-term predictor of "
"worsening hypoxaemia or acidosis."
))
story.append(h3("Procedure"))
story += bullets([
"External FHR monitor (cardiotocograph) placed on the maternal abdomen",
"Standard duration: 20 minutes; extended to 40 minutes if initial period is non-reactive",
"Uterine activity recorded simultaneously",
"Vibroacoustic stimulation (VAS) may be used to convert quiet sleep state and shorten testing time",
])
story.append(h3("Reactive NST (Normal)"))
story.append(note_box([
"≥2 FHR accelerations of ≥15 bpm above baseline, lasting ≥15 seconds, within a 20–30 min window, associated with fetal movement",
"Reassures fetal well-being for approximately 1 week in most clinical settings",
"False-negative rate (fetal death within 1 week): ~1.9 per 1,000",
]))
story.append(h3("Gestational Age Modification (<32 weeks)"))
story.append(p(
"Accelerations of ≥10 bpm lasting ≥10 seconds are accepted as reactive in the context of normal FHR "
"variability for fetuses <32 weeks' gestation, acknowledging that younger fetuses produce smaller accelerations."
))
story.append(h3("Non-Reactive NST"))
story.append(p(
"Defined by failure to meet reactivity criteria within the allotted time (up to 40–60 minutes depending "
"on protocol). Approximately 10–12% of third-trimester fetuses are non-reactive at 30 minutes; this "
"falls below 6% by 40 minutes. A non-reactive NST is <b>not diagnostic of compromise</b> — it triggers "
"further assessment (full BPP or CST)."
))
story.append(h4("Common causes of non-reactive NST without true compromise:"))
story += bullets([
"Fetal sleep cycle (most common cause)",
"Prematurity (<28 weeks)",
"Fetal CNS anomalies",
"Maternal medications (narcotics, magnesium sulfate, steroids)",
"Fetal anaemia",
])
story.append(h3("Vibroacoustic Stimulation (VAS)"))
story.append(p(
"Vibration sense is fully developed by 22–24 weeks; auditory response by ~26 weeks. An electromechanical "
"device applied to the maternal abdomen produces broad-band atonal white noise (0.1–10 kHz), provoking "
"fetal state change from quiet sleep to active sleep. VAS shortens monitoring time, converts many "
"non-reactive NSTs without pathological significance, but carries rare risk of fetal bradycardia and "
"has limited long-term safety data."
))
story.append(sp(8))
# ── 2.3 CST / OCT
story.append(h2("2.3 Contraction Stress Test (CST) and Oxytocin Challenge Test (OCT)"))
story.append(p(
"The CST evaluates <b>uteroplacental reserve</b> by examining the FHR response to uterine contractions, "
"which transiently reduce intervillous blood flow. A fetus with marginal placental reserve may maintain "
"adequate oxygenation at rest but decompensate during contractions, producing late decelerations."
))
story.append(h3("Methods of Inducing Contractions"))
story += bullets([
"<b>Nipple stimulation CST:</b> Patient stimulates one nipple for 2-minute intervals, harnessing endogenous oxytocin release",
"<b>Oxytocin Challenge Test (OCT):</b> IV oxytocin infusion titrated to achieve 3 contractions in 10 minutes, each lasting 40–60 seconds",
])
story.append(h3("Interpretation"))
cst_data = [
[Paragraph("<b>Result</b>", S["body"]), Paragraph("<b>Definition</b>", S["body"]), Paragraph("<b>Clinical Action</b>", S["body"])],
["Negative (Normal)", "No late or significant variable decelerations", "Reassuring — repeat in 1 week"],
["Positive (Abnormal)", "Late decelerations following ≥50% of contractions", "High-risk — delivery planning or intensive monitoring"],
["Equivocal-Suspicious", "Intermittent late or significant variable decelerations", "Repeat test"],
["Equivocal-Hyperstimulation", "Decelerations with contractions >90 sec or >2 in 10 min", "Repeat when uterine activity normalises"],
["Unsatisfactory", "<3 contractions in 10 min or uninterpretable tracing", "Repeat test"],
]
story.append(make_table(cst_data[0], cst_data[1:], [4*cm, 7*cm, 6*cm]))
story.append(h3("Performance and Limitations"))
story += bullets([
"False-positive rate: <b>~30%</b> — leading to unnecessary interventions",
"False-negative rate: <b>~0.04%</b> — very low",
"Now used less commonly than the BPP or modified BPP due to contraindications and high false-positive rate",
])
story.append(h4("Contraindications:"))
story += bullets([
"Preterm labour risk (cervical incompetence, prior preterm labour)",
"Preterm premature rupture of membranes (PPROM)",
"Multiple gestation with complications",
"Prior classical (vertical) uterine incision",
"Placenta praevia or vasa praevia",
], style="sub_bullet")
story.append(PageBreak())
# ── 2.4 BPP
story.append(section_header("2", "Methods of Surveillance (continued)"))
story.append(sp(10))
story.append(h2("2.4 Biophysical Profile (BPP)"))
story.append(p(
"Introduced by Manning in 1980, the BPP combines <b>four acute neurological markers</b> (reflecting "
"real-time CNS oxygenation) with <b>one chronic marker</b> (amniotic fluid volume, reflecting days-to-weeks "
"of cumulative placental function). It is conceptually similar to performing an in utero neurological "
"examination of the infant."
))
story.append(h3("The Five BPP Variables"))
bpp_data = [
[Paragraph("<b>Variable</b>", S["body"]), Paragraph("<b>Score 2 (Normal)</b>", S["body"]), Paragraph("<b>Score 0 (Abnormal)</b>", S["body"]), Paragraph("<b>Marker Type</b>", S["body"])],
["Fetal Breathing Movement (FBM)",
"≥30 sec of sustained FBM in 30 min of observation",
"<30 sec of FBM in 30 min",
"Acute"],
["Fetal Body Movement (FM)",
"≥3 gross body movements in 30 min (simultaneous limb+trunk = 1 movement)",
"≤2 gross movements in 30 min",
"Acute"],
["Fetal Tone (FT)",
"≥1 episode of limb extension → rapid return to flexion; or spine extension/flexion",
"Fetus in semi/full extension; no return to flexion; absent movement = absent tone",
"Acute"],
["FHR Reactivity (NST)",
"≥2 accelerations ≥15 bpm × ≥15 sec with movement in 30 min",
"<2 qualifying accelerations during observation",
"Acute"],
["Amniotic Fluid Volume (AFV)",
"Pocket ≥2 cm in two perpendicular planes (or AFI ≥5 cm)",
"Largest pocket <2 cm in two perpendicular planes",
"Chronic"],
]
story.append(make_table(bpp_data[0], bpp_data[1:], [3.5*cm, 5.5*cm, 4.5*cm, 3.5*cm]))
story.append(p("<b>Maximum score: 10/10 | Minimum: 0/10</b>"))
story.append(sp(6))
story.append(h3("BPP Score Interpretation and Management"))
mgmt_data = [
[Paragraph("<b>Score</b>", S["body"]), Paragraph("<b>Risk of Fetal Acidosis</b>", S["body"]), Paragraph("<b>Management</b>", S["body"])],
["10/10 or 8/10 (normal AFV)", "Very low (<1 in 1,000)", "Reassured — routine follow-up per risk profile"],
["8/10 (abnormal AFV)", "Moderate", "Deliver if ≥36 weeks; intensive monitoring if preterm"],
["6/10", "Equivocal (variable)", "Deliver if ≥36 wks or mature; repeat in 4–6 hrs if preterm"],
["4/10", "High", "Deliver unless remote from viability"],
["2/10 or 0/10", "Very high", "Deliver regardless of gestational age"],
]
story.append(make_table(mgmt_data[0], mgmt_data[1:], [4*cm, 5*cm, 8*cm]))
story.append(sp(6))
story.append(note_box([
"⚠ Oligohydramnios constitutes an abnormal BPP regardless of the overall score.",
"Even if all 4 acute markers are normal (score 8), the presence of oligohydramnios mandates further evaluation and often delivery.",
]))
story.append(sp(8))
story.append(h3("Why Combine Variables? (Manning's Planning Score)"))
story.append(p(
"The figure below shows that the full five-variable BPP (the 'Planning Score') achieves the lowest rate "
"of adverse neonatal outcomes — statistically significantly better than any single variable alone."
))
story += embed_image(img_bpp, width_cm=14,
caption="Figure: Prediction of adverse outcome (5-min Apgar <7) by individual BPP variables (left) vs. combined variables (right). "
"The full Planning Score (all 5 variables) achieves the lowest adverse outcome rate. "
"AFV, Amniotic fluid volume; FBM, fetal breathing movement; FM, fetal movement; FT, fetal tone; NST, nonstress test. "
"(Source: Creasy & Resnik's Maternal-Fetal Medicine)")
story.append(h3("Pathophysiology of Low BPP in Chronic Hypoxia"))
story.append(p(
"When the fetus experiences sustained placental insufficiency, cardiac output is redistributed away from "
"non-vital organs (kidneys, lungs) toward vital organs (brain, heart, adrenals). Decreased renal perfusion "
"reduces urine production, leading to oligohydramnios. A low AFI may be the <b>earliest chronic marker</b> "
"of placental insufficiency."
))
story.append(sp(8))
# ── 2.5 Modified BPP
story.append(h2("2.5 Modified Biophysical Profile (Modified BPP)"))
story.append(p(
"A practical two-component test combining:"
))
story += bullets([
"<b>NST</b> — acute marker of CNS/cardiovascular function",
"<b>Amniotic Fluid Index (AFI)</b> — chronic marker of placental function",
])
story.append(note_box([
"Normal modified BPP: Reactive NST + AFI ≥5 cm",
"Abnormal modified BPP: Non-reactive NST OR AFI <5 cm → proceed to full BPP or CST",
]))
story.append(p(
"The modified BPP is the most widely used first-line outpatient surveillance tool. It requires less time "
"than the full BPP, while combining both acute and chronic markers. It performs comparably to the full "
"BPP for predicting fetal compromise in most high-risk populations."
))
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 3 — Doppler Velocimetry
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("3", "Doppler Velocimetry"))
story.append(sp(10))
story.append(p(
"Doppler ultrasound assesses blood flow velocity waveforms in specific fetal and maternal vessels. "
"It provides functional information about placental vascular resistance and the fetal circulatory "
"adaptation to compromise. The Cochrane systematic review (Alfirevic et al. 2017, CD007529) confirmed "
"that umbilical artery Doppler in high-risk pregnancies reduces perinatal mortality."
))
story.append(sp(4))
story.append(h2("3.1 Umbilical Artery (UA) Doppler"))
story.append(p(
"The umbilical artery supplies the placenta. As placental vascular resistance rises due to obliterative "
"vasculopathy (e.g., in FGR or preeclampsia), diastolic flow progressively diminishes."
))
story.append(h3("Waveform Indices"))
story += bullets([
"<b>S/D ratio</b> (systolic:diastolic): normally falls with advancing gestation as placental resistance decreases",
"<b>Resistance Index (RI)</b> = (S − D) / S",
"<b>Pulsatility Index (PI)</b> = (S − D) / mean velocity",
])
story.append(h3("Spectrum of Abnormality — in order of severity"))
ua_data = [
[Paragraph("<b>Finding</b>", S["body"]), Paragraph("<b>Pathological Significance</b>", S["body"]), Paragraph("<b>Clinical Implication</b>", S["body"])],
["Elevated S/D ratio / high PI or RI", "Increased placental resistance; ~30% of villous tree obliterated", "Intensify surveillance; serial Doppler"],
["Absent End-Diastolic Velocity (AEDV)", "~60–70% of villous tree obliterated", "High risk — daily monitoring; consider DV Doppler; plan delivery timing"],
["Reversed End-Diastolic Velocity (REDV)", ">70% obliteration; imminent cardiac decompensation", "Very high risk — hospitalise; REDV + abnormal DV = near-immediate delivery"],
]
story.append(make_table(ua_data[0], ua_data[1:], [5.5*cm, 6*cm, 5.5*cm]))
story.append(p(
"AEDV and REDV are independently associated with substantially increased rates of intraventricular "
"haemorrhage, necrotising enterocolitis, need for transfusion, and perinatal mortality. The frequency "
"of all these outcomes rises progressively from elevated indices → AEDV → REDV."
))
story.append(sp(6))
story.append(h2("3.2 Middle Cerebral Artery (MCA) Doppler"))
story.append(p(
"The MCA is short, straight, and anatomically consistent, making it ideal for Doppler measurement. "
"The Doppler angle can be minimised, enabling direct interpretation of peak systolic velocity (PSV). "
"Ideal sampling location: 2 mm past the origin of the MCA from the internal carotid artery."
))
story.append(h3("Application 1 — Fetal Anemia Assessment"))
story.append(p(
"As fetal haematocrit falls and oxygen-carrying capacity decreases, cardiac output increases and blood "
"viscosity decreases, raising the MCA PSV. An <b>MCA PSV >1.5 multiples of the median (MoM)</b> for "
"gestational age predicts moderate-to-severe fetal anaemia with high sensitivity (~88%) and specificity "
"(~82%), replacing the need for invasive amniocentesis for optical density measurement."
))
story.append(h3("Application 2 — Brain Sparing (Cerebral Redistribution)"))
story.append(p(
"In fetal hypoxia from placental insufficiency, cardiac output is redistributed toward the brain, "
"increasing diastolic MCA velocities and <b>lowering</b> the MCA PI/RI. Key abnormalities:"
))
story += bullets([
"<b>Low MCA PI</b> (especially <5th percentile for gestational age): indicates increased cerebral perfusion",
"<b>Cerebro-placental ratio (CPR)</b> = MCA PI / UA PI: falls below 1.0 (or <5th percentile) in redistribution",
"CPR is a sensitive early marker of circulatory compromise, detecting redistribution before overt changes appear in either vessel alone",
])
story.append(p(
"Cerebral blood flow (MCA) is also responsive to maternal oxygen administration (increases resistance), "
"CO₂ (decreases resistance), and nicotine (increases resistance with reduced responsiveness)."
))
story.append(sp(4))
story += embed_image(img_mca, width_cm=13,
caption="Figure: Serial MCA Doppler PSV measurements in a fetus with severe anti-D isoimmune anaemia. "
"Top (pre-transfusion): PSV 60 cm/s → haematocrit 19. "
"Middle (post-transfusion): PSV fell to 41 cm/s → haematocrit 44. "
"Bottom (recurrence): PSV rose to 62 cm/s → haematocrit 20. "
"(Source: Creasy & Resnik's Maternal-Fetal Medicine)")
story.append(h2("3.3 Ductus Venosus (DV) Doppler"))
story.append(p(
"The ductus venosus is a shunt between the umbilical vein and the inferior vena cava, bypassing "
"hepatic circulation. It preferentially delivers oxygenated blood to the foramen ovale and hence "
"to the coronary and cerebral circulations."
))
story.append(h3("Normal Waveform"))
story.append(p("Triphasic: S wave (ventricular systole), D wave (passive ventricular filling), a wave (atrial contraction) — all with <b>forward flow</b>."))
story.append(h3("Abnormal Findings"))
story += bullets([
"<b>Absent a-wave:</b> elevated cardiac end-diastolic pressure — early venous compromise",
"<b>Reversed a-wave:</b> severely elevated venous pressures, cardiac decompensation — associated with very high perinatal mortality",
])
story.append(p(
"DV Doppler is particularly useful in severe FGR at very preterm gestations, guiding the timing of "
"delivery. Progressive deterioration from abnormal UA → abnormal DV typically occurs over days to "
"weeks, providing a window for corticosteroid administration and optimised delivery planning."
))
story.append(sp(8))
story.append(h2("3.4 Uterine Artery Doppler"))
story.append(p(
"Assesses maternal spiral artery remodelling by trophoblastic invasion. After 24 weeks, resistance "
"normally falls as trophoblasts remodel spiral arteries and notching disappears."
))
story += bullets([
"<b>Persistent bilateral uterine artery notching</b> and/or high PI at 20–24 weeks predicts subsequent preeclampsia (sensitivity ~40–60% for severe disease), FGR, and placental abruption",
"Used primarily as a <b>second-trimester screening tool</b> for placental complications rather than real-time fetal surveillance",
])
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 4 — Amniotic Fluid Assessment
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("4", "Amniotic Fluid Assessment"))
story.append(sp(10))
story.append(p(
"Amniotic fluid volume reflects fetal urine production (renal perfusion) and swallowing. It is a "
"'memory' of placental function over the preceding weeks — a chronic marker of fetal oxygenation. "
"A low AFI may be the earliest chronic marker of placental insufficiency."
))
story.append(h2("Measurement Methods"))
story.append(h3("1. Amniotic Fluid Index (AFI) — Four-Quadrant Technique"))
story += bullets([
"Sum of the deepest vertical pocket in each of four uterine quadrants (cm)",
"Normal range: 5–25 cm (varies by gestational age, peaks ~32–34 weeks)",
"<b>Oligohydramnios:</b> AFI <5 cm",
"<b>Borderline:</b> AFI 5–8 cm",
"<b>Polyhydramnios:</b> AFI >25 cm",
])
story.append(h3("2. Single Deepest Pocket (SDP) / Maximum Vertical Pocket (MVP)"))
story += bullets([
"Measures the single largest vertical pocket without cord or fetal parts",
"Normal: 2–8 cm",
"<b>Oligohydramnios:</b> SDP <2 cm; <b>Polyhydramnios:</b> SDP >8 cm",
"Preferred by ISUOG and many European centres as it reduces unnecessary intervention rate",
])
story.append(note_box([
"BPP uses a 2 cm in two perpendicular planes criterion (a 2×2 cm pocket). Even if AFI <5 cm technically, "
"if a 2×2 cm pocket is identified, the BPP AFV component scores 2. However, oligohydramnios on any "
"measurement remains clinically significant and warrants evaluation.",
]))
story.append(h2("Causes of Oligohydramnios"))
story += bullets([
"Placental insufficiency (FGR) — most common in the third trimester",
"Fetal renal agenesis or obstructive uropathy (bilateral renal pathology)",
"Preterm premature rupture of membranes (PPROM)",
"Post-dates pregnancy (≥42 weeks)",
"NSAID exposure (prostaglandin inhibition closes ductus arteriosus; also reduces renal flow)",
"Uteroplacental insufficiency from any cause",
])
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 5 — Indications
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("5", "Indications for Antepartum Surveillance"))
story.append(sp(10))
story.append(p(
"Formal antepartum testing is typically initiated from 32–34 weeks, or earlier in severe cases. "
"The onset timing is individualised based on the severity of the condition and the anticipated "
"balance of risks from fetal compromise vs. prematurity."
))
ind_data = [
[Paragraph("<b>Category</b>", S["body"]), Paragraph("<b>Indications</b>", S["body"])],
["Maternal Conditions",
"• Hypertensive disorders (chronic HTN, preeclampsia)\n• Pre-gestational diabetes or poorly controlled GDM\n"
"• Renal disease (chronic or acute)\n• Systemic lupus erythematosus / antiphospholipid syndrome\n"
"• Sickle cell disease / haemoglobinopathies\n• Hyperthyroidism / significant cardiac disease"],
["Obstetric / Fetal Conditions",
"• Fetal growth restriction (FGR) — primary indication\n• Decreased fetal movement\n"
"• Post-dates pregnancy (≥41 weeks)\n• Multiple gestation with growth discordance\n"
"• Oligohydramnios or polyhydramnios\n• Fetal anomalies requiring monitoring\n"
"• Isoimmunisation (Rh or other blood group antibodies)\n• Prior stillbirth\n"
"• Unexplained third-trimester bleeding\n• Conservatively managed preterm labour"],
]
t = Table(ind_data, colWidths=[5*cm, 12*cm], repeatRows=1)
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TABLE_HDR),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#b0c8c8")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, TABLE_ALT]),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
]))
story.append(t)
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 6 — Integrated Algorithm
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("6", "Integrated Testing Algorithms"))
story.append(sp(10))
story.append(p(
"No single test satisfies all criteria for ideal fetal surveillance. Multiple-variable approaches are "
"more reliable. The following represents a standard stepwise approach used in most high-risk obstetric centres."
))
story.append(h2("Stepwise Algorithm — Outpatient High-Risk Surveillance"))
alg_data = [
[Paragraph("<b>Step</b>", S["body"]), Paragraph("<b>Test</b>", S["body"]), Paragraph("<b>Result</b>", S["body"]), Paragraph("<b>Next Action</b>", S["body"])],
["1", "Modified BPP\n(NST + AFI)\nWeekly or twice-weekly",
"Reactive NST + AFI ≥5 cm",
"Reassuring — repeat per schedule"],
["1 (abnormal)", "Modified BPP",
"Non-reactive NST OR AFI <5 cm",
"Proceed to full BPP"],
["2", "Full BPP\n(5 variables, 30-min US)",
"≥8/10 with normal AFV",
"Continue monitoring schedule"],
["2 (equivocal)", "Full BPP",
"6/10",
"Repeat in 4–6 hrs; if still 6/10 → deliver if ≥36 wks"],
["2 (abnormal)", "Full BPP",
"≤4/10",
"Deliver (unless extremely preterm — weigh carefully)"],
["3 (FGR)", "UA Doppler",
"AEDV",
"Daily monitoring; corticosteroids if <34 wks; add DV Doppler"],
["3 (FGR)", "UA Doppler",
"REDV",
"Hospitalise; near-immediate delivery with steroids if <34 wks"],
["4 (FGR)", "DV Doppler",
"Reversed DV a-wave",
"Immediate delivery (give steroids if <34 wks, time allowing)"],
["Anaemia", "MCA PSV",
">1.5 MoM",
"Fetal blood sampling (cordocentesis) ± intrauterine transfusion"],
]
story.append(make_table(alg_data[0], alg_data[1:], [3*cm, 4*cm, 4.5*cm, 5.5*cm]))
story.append(sp(8))
story.append(h2("Testing Frequency"))
freq_data = [
[Paragraph("<b>Frequency</b>", S["body"]), Paragraph("<b>Indication</b>", S["body"])],
["Once weekly", "Most high-risk conditions (controlled diabetes, mild FGR, prior stillbirth)"],
["Twice weekly", "Severe FGR, post-dates ≥41 wks, multiple gestation with complications, uncontrolled diabetes, preeclampsia"],
["Daily or near-daily", "AEDV on UA Doppler, severe preeclampsia remote from term, severe FGR with unstable status"],
]
story.append(make_table(freq_data[0], freq_data[1:], [4.5*cm, 12.5*cm]))
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 7 — Special Situations
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("7", "Special Clinical Situations"))
story.append(sp(10))
story.append(h2("7.1 Fetal Growth Restriction (FGR)"))
story.append(p(
"The most common indication for antepartum surveillance. UA Doppler is the primary tool. "
"Early-onset severe FGR (<32 weeks) typically follows a predictable deterioration sequence:"
))
fgr_data = [
[Paragraph("<b>Stage</b>", S["body"]), Paragraph("<b>Finding</b>", S["body"]), Paragraph("<b>Typical Timing</b>", S["body"])],
["Early compromise", "Elevated UA PI / abnormal UA S/D ratio", "Weeks before delivery"],
["Brain sparing", "Low MCA PI; CPR <1.0; redistribution", "Weeks before delivery"],
["Advanced", "AEDV (absent end-diastolic velocity) on UA", "Days to 2 weeks before delivery"],
["Late/critical", "REDV on UA; abnormal DV (absent/reversed a-wave)", "Hours to days before delivery"],
["Terminal", "Reversed DV a-wave; umbilical vein pulsations; abnormal NST/BPP", "Immediate"],
]
story.append(make_table(fgr_data[0], fgr_data[1:], [4*cm, 7*cm, 6*cm]))
story.append(p(
"The FIGO initiative on FGR (Melamed et al. 2021, IJGO) provides detailed guidance on staging and "
"delivery timing based on Doppler findings and gestational age."
))
story.append(sp(8))
story.append(h2("7.2 Post-dates Pregnancy (≥41 Weeks)"))
story += bullets([
"Weekly NST + AFI (modified BPP) or twice-weekly testing recommended",
"Oligohydramnios alone (AFI <5 cm or SDP <2 cm) is an indication for delivery at ≥41 weeks",
"NST in isolation may miss some adverse outcomes; AFI adds important value",
"Most guidelines recommend delivery by 41–42 weeks regardless of surveillance results",
])
story.append(sp(8))
story.append(h2("7.3 Fetal Isoimmunisation"))
story += bullets([
"Serial MCA PSV measurements every 1–2 weeks to detect worsening anaemia",
"MCA PSV >1.5 MoM for gestational age → proceed to fetal blood sampling (cordocentesis)",
"If anaemia confirmed (Hb <2 SD below mean for gestation), intrauterine transfusion (IUT) performed",
"Post-transfusion PSV normalises; rise again indicates recurrence — tracked serially",
])
story.append(sp(8))
story.append(h2("7.4 Multiple Gestation"))
story += bullets([
"<b>Monochorionic twins:</b> surveillance from 16 weeks for twin-to-twin transfusion syndrome (TTTS) with serial ultrasound every 2 weeks",
"<b>Growth-discordant twins:</b> same UA Doppler protocol as singleton FGR, applied to the smaller twin",
"<b>TTTS:</b> staging by Quintero criteria; UA, DV, and MCA Doppler all contribute to management",
"Dichorionic twins: routine growth ultrasound every 4 weeks; more frequent if discordance >20%",
])
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 8 — Performance Summary
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("8", "Test Performance and Limitations"))
story.append(sp(10))
perf_data = [
[Paragraph("<b>Test</b>", S["body"]), Paragraph("<b>False-Negative Rate</b>", S["body"]),
Paragraph("<b>False-Positive Rate</b>", S["body"]), Paragraph("<b>Key Limitations</b>", S["body"])],
["Kick counts", "Not well quantified", "Variable", "Subjective; does not identify cause; affected by maternal perception"],
["NST alone", "~1.9 per 1,000/week", "~80% of non-reactive results", "Sleep states, medications, prematurity, CNS anomalies"],
["CST / OCT", "~0.04%", "~30%", "Contraindications, time-consuming, high false-positive rate"],
["Full BPP", "~0.7 per 1,000/week", "Lower than NST alone", "Time-consuming (~30 min); operator-dependent; sleep states"],
["Modified BPP", "~0.8 per 1,000/week", "Intermediate", "Only two variables; still affected by sleep/medications"],
["UA Doppler", "Low in high-risk (FGR)", "Moderate in low-risk populations", "Not useful for all conditions; requires expertise"],
["MCA PSV", "~12% for severe anaemia", "~18%", "Angle-dependent; not useful outside anaemia/redistribution contexts"],
["DV Doppler", "Very low for imminent compromise", "Low", "Technically demanding; requires expertise"],
]
story.append(make_table(perf_data[0], perf_data[1:], [3*cm, 3.5*cm, 4*cm, 6.5*cm]))
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════
# SECTION 9 — Summary Reference Table
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("9", "Master Summary Reference Table"))
story.append(sp(10))
summary_data = [
[Paragraph("<b>Method</b>", S["body"]), Paragraph("<b>What It Measures</b>", S["body"]),
Paragraph("<b>Acute / Chronic</b>", S["body"]), Paragraph("<b>Primary Use</b>", S["body"])],
["Kick counts", "Gross fetal movement", "Acute", "Daily, all at-risk patients"],
["NST", "FHR reactivity (CNS-CVS coupling)", "Acute", "Weekly/twice-weekly first-line screening"],
["CST / OCT", "Uteroplacental reserve", "Acute", "Adjunct when NST non-reactive; less used now"],
["Full BPP", "5 biophysical variables", "Acute + Chronic", "Non-reactive NST; FGR; abnormal modified BPP"],
["Modified BPP", "NST + AFI", "Acute + Chronic", "First-line outpatient surveillance; most practical"],
["UA Doppler", "Placental vascular resistance", "Intermediate", "FGR, hypertension, collagen vascular disease"],
["MCA PSV", "Fetal haematocrit/anaemia risk", "Acute", "Isoimmunisation, hydrops, suspected anaemia"],
["MCA PI / CPR", "Brain sparing / redistribution", "Intermediate", "FGR with placental insufficiency"],
["DV Doppler", "Venous/cardiac function", "Late/severe", "Late-stage FGR; timing delivery decision"],
["Uterine artery Doppler", "Maternal spiral artery remodelling", "Chronic/predictive", "20–24 week screening for placental complications"],
["AFI / SDP", "Amniotic fluid (cumulative renal perfusion)", "Chronic", "Part of BPP; oligohydramnios workup"],
]
story.append(make_table(summary_data[0], summary_data[1:], [4*cm, 5*cm, 3.5*cm, 4.5*cm]))
story.append(sp(12))
# Sources
story.append(hr())
story.append(Paragraph("<b>Sources and References</b>", S["h3"]))
story += [
Paragraph("1. Creasy & Resnik's Maternal-Fetal Medicine: Principles and Practice, 9th edition — Chapter 32 (Antepartum Fetal Assessment), Chapter 35 (Haemolytic Disease)", S["source"]),
Paragraph("2. Pfenninger and Fowler's Procedures for Primary Care, 3rd edition — Chapter 172 (Fetal Assessment: Biophysical Profile)", S["source"]),
Paragraph("3. Alfirevic Z, Stampalija T, Dowsell T. Fetal and umbilical Doppler ultrasound in high-risk pregnancies. Cochrane Database Syst Rev. 2017;6(6):CD007529. PMID: 28613398", S["source"]),
Paragraph("4. Melamed N, Baschat A, Yinon Y, et al. FIGO initiative on fetal growth: best practice advice for screening, diagnosis, and management of fetal growth restriction. Int J Gynaecol Obstet. 2021;152(Suppl 1):3–57. PMID: 33740264", S["source"]),
Paragraph("5. Manning FA, Platt LD. Antepartum fetal evaluation: Development of a fetal biophysical profile. Am J Obstet Gynecol. 1980;136:787–795.", S["source"]),
]
# ── Build ──────────────────────────────────────────────────────────────────
print("Building PDF...")
doc.build(
story,
onFirstPage=on_first_page,
onLaterPages=on_later_pages,
)
print(f"Done → {OUTPUT}")
if __name__ == "__main__":
build()
cd /tmp/workspace/antepartum-fetal-monitoring && python build_pdf.py 2>&1
~/antepartum-fetal-monitoring/build_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/antepartum-fetal-monitoring/build_pdf.py'
cd /tmp/workspace/antepartum-fetal-monitoring && python build_pdf.py 2>&1
ls -lh /tmp/workspace/antepartum-fetal-monitoring/Antepartum_Fetal_Monitoring.pdf
Antepartum Fetal Monitoring
PDF Document · PDF
| Section | Content |
|---|---|
| Cover page | Navy/teal gradient with title, subtitle, source credits |
| 1. Physiological Basis | CNS hierarchy table, ideal monitoring criteria, FHR development |
| 2. Surveillance Methods | Kick counts, NST (with VAS), CST/OCT interpretation table, Full BPP variable table + management table, Modified BPP |
| 3. Doppler Velocimetry | UA Doppler severity spectrum, MCA PSV/CPR/brain sparing, DV waveform, Uterine artery |
| 4. Amniotic Fluid | AFI vs SDP, oligohydramnios causes |
| 5. Indications | Maternal and obstetric/fetal conditions table |
| 6. Algorithms | Stepwise decision algorithm table, testing frequency table |
| 7. Special Situations | FGR deterioration sequence, post-dates, isoimmunisation, multiples |
| 8. Test Performance | False-positive/negative rates and limitations per test |
| 9. Master Summary | All 11 methods in one reference table |