Make comprehensive data of breast cancer from multiple standard textbooks (Bailey & Love 28th edition and Sabiston) with figures.
"breast cancer" AND management
| Section | Primary Source |
|---|---|
| Epidemiology | B&L Ch. 58 |
| Anatomy (with 2 diagrams) | Sabiston Ch. 68 |
| Risk Factors (full table) | B&L Table 58.3 |
| Pathology - all histological types | B&L + Sabiston |
| Nottingham grading system | Both |
| Molecular classification (5 subtypes) | B&L Table 58.4 + Sabiston |
| Clinical features + 2 clinical photos | B&L |
| Triple assessment & investigations | B&L |
| TNM Staging (AJCC 8th Ed.) | B&L + Sabiston |
| Surgical treatment (BCS, mastectomy, axilla) | Sabiston |
| Radiotherapy | Sabiston |
| Systemic therapy (chemo, endocrine, HER2, CDK4/6i) | Both |
| Chemoprevention (4-drug table with trials) | Sabiston |
| Special scenarios (hereditary, pregnancy, DCIS, inflammatory, male, recurrence, metastatic) | Both |
| Reconstruction | B&L |
| Screening + Prognosis + Multigene assays | Both |
Make pdf of this
ls /home/daytona/workspace/ && mkdir -p /home/daytona/workspace/breast-cancer-pdf
~/breast-cancer-pdf/generate_pdf.py
#!/usr/bin/env python3
"""Generate a comprehensive breast cancer PDF from Bailey & Love + Sabiston content."""
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, Image, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus.flowables import Flowable
import urllib.request
import os
import io
# ── Output path ──────────────────────────────────────────────────────────────
OUTPUT = "/home/daytona/workspace/breast-cancer-pdf/Breast_Cancer_Comprehensive_Reference.pdf"
IMG_DIR = "/home/daytona/workspace/breast-cancer-pdf/images"
os.makedirs(IMG_DIR, exist_ok=True)
# ── Download images ──────────────────────────────────────────────────────────
IMAGES = {
"anatomy_breast.png": "https://cdn.orris.care/cdss_images/a4d4069712df041821571dfb308c52e6ebf541afb9976f23c8c860c10c1f05a0.png",
"tdlu_diagram.png": "https://cdn.orris.care/cdss_images/b845f6af92644f552b1a45fa82cbd900948b40f1159f127632a589b337f2dda2.png",
"inflammatory_ca.png": "https://cdn.orris.care/cdss_images/7c1307190782fab9bf42e74ad602793b17aa812d69dbc97d345f39a5c6dfc5c8.png",
"idc_histology.png": "https://cdn.orris.care/cdss_images/a7976dc8796d45630b8035ab7b9d9a51d19ab1a1da0a00fe3a1a2be2d02c2b2c.png",
}
for fname, url in IMAGES.items():
dest = os.path.join(IMG_DIR, fname)
if not os.path.exists(dest):
print(f"Downloading {fname}...")
try:
urllib.request.urlretrieve(url, dest)
print(f" OK: {dest}")
except Exception as e:
print(f" FAILED: {e}")
# ── Colour palette ───────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1a3a5c")
MID_BLUE = colors.HexColor("#2e6da4")
LIGHT_BLUE = colors.HexColor("#dce9f5")
ACCENT = colors.HexColor("#c0392b") # red accent for headings
TABLE_HDR = colors.HexColor("#2e6da4")
TABLE_ALT = colors.HexColor("#f0f5fb")
WHITE = colors.white
GRAY_TEXT = colors.HexColor("#444444")
BORDER = colors.HexColor("#aec6e0")
# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
TITLE_STYLE = S("MainTitle",
fontName="Helvetica-Bold", fontSize=22, textColor=WHITE,
alignment=TA_CENTER, spaceAfter=4)
SUBTITLE_STYLE = S("Subtitle",
fontName="Helvetica", fontSize=11, textColor=colors.HexColor("#cce0f5"),
alignment=TA_CENTER, spaceAfter=2)
H1 = S("H1",
fontName="Helvetica-Bold", fontSize=14, textColor=WHITE,
backColor=DARK_BLUE, borderPadding=(6,8,6,8),
spaceBefore=14, spaceAfter=6)
H2 = S("H2",
fontName="Helvetica-Bold", fontSize=12, textColor=DARK_BLUE,
spaceBefore=10, spaceAfter=4, borderPadding=(2,0,2,0))
H3 = S("H3",
fontName="Helvetica-BoldOblique", fontSize=10.5, textColor=MID_BLUE,
spaceBefore=7, spaceAfter=3)
BODY = S("Body",
fontName="Helvetica", fontSize=9.5, textColor=GRAY_TEXT,
leading=14, spaceAfter=5, alignment=TA_JUSTIFY)
BODY_B = S("BodyBold",
fontName="Helvetica-Bold", fontSize=9.5, textColor=GRAY_TEXT,
leading=14, spaceAfter=5)
BULLET = S("Bullet",
fontName="Helvetica", fontSize=9.5, textColor=GRAY_TEXT,
leading=13, leftIndent=14, firstLineIndent=-10, spaceAfter=3,
alignment=TA_LEFT)
BULLET2 = S("Bullet2",
fontName="Helvetica", fontSize=9, textColor=GRAY_TEXT,
leading=12, leftIndent=26, firstLineIndent=-10, spaceAfter=2)
CAPTION = S("Caption",
fontName="Helvetica-Oblique", fontSize=8.5, textColor=colors.HexColor("#555555"),
alignment=TA_CENTER, spaceAfter=8, spaceBefore=3)
SOURCE = S("Source",
fontName="Helvetica-Oblique", fontSize=8, textColor=colors.HexColor("#777777"),
alignment=TA_RIGHT, spaceAfter=3)
NOTE = S("Note",
fontName="Helvetica-Oblique", fontSize=8.5, textColor=colors.HexColor("#555555"),
backColor=colors.HexColor("#fff8e1"), borderPadding=6,
borderColor=colors.HexColor("#f0c040"), borderWidth=1,
leading=12, spaceAfter=8)
# ── Helper: table style ──────────────────────────────────────────────────────
def make_table(data, col_widths, hdr_rows=1, alt=True):
ts = TableStyle([
("BACKGROUND", (0,0), (-1, hdr_rows-1), TABLE_HDR),
("TEXTCOLOR", (0,0), (-1, hdr_rows-1), WHITE),
("FONTNAME", (0,0), (-1, hdr_rows-1), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1, hdr_rows-1), 9),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("FONTNAME", (0,hdr_rows), (-1,-1), "Helvetica"),
("FONTSIZE", (0,hdr_rows), (-1,-1), 8.5),
("TEXTCOLOR", (0,hdr_rows), (-1,-1), GRAY_TEXT),
("ROWBACKGROUNDS", (0,hdr_rows), (-1,-1),
[TABLE_ALT, WHITE] if alt else [WHITE]),
("GRID", (0,0), (-1,-1), 0.5, BORDER),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("ROWBACKGROUNDS", (0,0), (-1, hdr_rows-1), [TABLE_HDR]),
])
t = Table(data, colWidths=col_widths, repeatRows=hdr_rows)
t.setStyle(ts)
return t
# ── Helper: section banner ────────────────────────────────────────────────────
def section_banner(number, title):
return Paragraph(f"<b>{number}. {title}</b>", H1)
def h2(text):
return Paragraph(f"<b>{text}</b>", H2)
def h3(text):
return Paragraph(text, H3)
def body(text):
return Paragraph(text, BODY)
def bullet(text, level=1):
st = BULLET if level == 1 else BULLET2
return Paragraph(f"• {text}", st)
def sp(h=6):
return Spacer(1, h)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=BORDER, spaceAfter=4, spaceBefore=4)
def img(path, width, caption=""):
items = []
if os.path.exists(path):
im = Image(path, width=width, kind="proportional")
im.hAlign = "CENTER"
items.append(im)
if caption:
items.append(Paragraph(caption, CAPTION))
return items
# ── Cover page builder ────────────────────────────────────────────────────────
class ColorRect(Flowable):
def __init__(self, w, h, fill_color, radius=4):
super().__init__()
self.width = w; self.height = h
self.fill_color = fill_color; self.radius = radius
def draw(self):
self.canv.setFillColor(self.fill_color)
self.canv.roundRect(0, 0, self.width, self.height, self.radius, fill=1, stroke=0)
def cover_elements(page_w, page_h):
"""Return flowables for a styled cover page."""
elems = []
elems.append(sp(60))
# Title box
box_w = page_w - 4*cm
box = Table([[Paragraph(
"<b>BREAST CANCER</b><br/>"
"<font size=14 color='#cce0f5'>Comprehensive Surgical Reference</font>",
S("CT", fontName="Helvetica-Bold", fontSize=26, textColor=WHITE, alignment=TA_CENTER, leading=34)
)]], colWidths=[box_w])
box.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
("LEFTPADDING", (0,0), (-1,-1), 20),
("RIGHTPADDING", (0,0), (-1,-1), 20),
("TOPPADDING", (0,0), (-1,-1), 28),
("BOTTOMPADDING", (0,0), (-1,-1), 28),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
elems.append(box)
elems.append(sp(18))
sources = [
["Primary Sources:"],
["Bailey & Love's Short Practice of Surgery, 28th Edition"],
["Sabiston Textbook of Surgery (The Biological Basis of Modern Surgical Practice)"],
]
src_table = Table([[Paragraph(
"<b>Primary Sources:</b><br/>"
"• Bailey & Love's Short Practice of Surgery, 28th Edition — Chapter 58: The Breast<br/>"
"• Sabiston Textbook of Surgery — Chapter 68: Diseases of the Breast",
S("ST", fontName="Helvetica", fontSize=10.5, textColor=DARK_BLUE, leading=16, alignment=TA_CENTER)
)]], colWidths=[box_w])
src_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_BLUE),
("LEFTPADDING", (0,0), (-1,-1), 18),
("RIGHTPADDING", (0,0), (-1,-1), 18),
("TOPPADDING", (0,0), (-1,-1), 14),
("BOTTOMPADDING", (0,0), (-1,-1), 14),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("BOX", (0,0), (-1,-1), 1, BORDER),
]))
elems.append(src_table)
elems.append(sp(20))
# Summary boxes
topics = [
"Epidemiology & Risk Factors", "Anatomy & Physiology",
"Pathology & Molecular Classification", "Staging (AJCC 8th Ed.)",
"Surgical Management", "Systemic Therapy",
"Radiotherapy", "Screening & Prognosis",
"Special Scenarios", "Breast Reconstruction",
]
rows = []
for i in range(0, len(topics), 2):
row = []
for j in range(2):
if i+j < len(topics):
row.append(Paragraph(f"✓ {topics[i+j]}",
S("TT", fontName="Helvetica", fontSize=9.5, textColor=DARK_BLUE, leading=13)))
else:
row.append("")
rows.append(row)
toc_box = Table(rows, colWidths=[box_w/2, box_w/2])
toc_box.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#f4f8fd")),
("GRID", (0,0), (-1,-1), 0.4, BORDER),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
elems.append(toc_box)
elems.append(sp(30))
elems.append(Paragraph("Compiled June 2026", S("D",
fontName="Helvetica-Oblique", fontSize=9, textColor=colors.HexColor("#888888"),
alignment=TA_CENTER)))
elems.append(PageBreak())
return elems
# ── Main content builder ──────────────────────────────────────────────────────
def build_content():
W = A4[0] - 4*cm # usable width
elems = []
# ── SECTION 1: EPIDEMIOLOGY ───────────────────────────────────────────────
elems.append(section_banner("1", "EPIDEMIOLOGY"))
elems.append(body("Breast cancer is the most frequent cancer among women worldwide, with an estimated <b>2.3 million new cases</b> diagnosed globally in 2020, representing approximately <b>25%</b> of all cancers in women."))
elems.append(sp(4))
epi_data = [
["Region", "Incidence (per 100,000 women)"],
["Middle Africa / East Asia", "27"],
["South Asia", "~28–35"],
["North America", "92"],
["Western Europe", "~85–90"],
]
elems.append(make_table(epi_data, [W*0.65, W*0.35]))
elems.append(sp(4))
for b_text in [
"In Western Europe: <b>~1 in 9</b> women will develop breast cancer (3–5% of all female deaths)",
"In resource-poor countries: <b>1 in 28</b> women; for every 2 diagnosed, 1 dies",
"Median age at presentation: ~<b>60 years</b> (UK/USA); ~<b>48 years</b> in South Asia",
"Breast cancer accounts for the <b>leading cause of cancer-related death</b> in women globally",
]:
elems.append(bullet(b_text))
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58</i>", SOURCE))
# ── SECTION 2: ANATOMY ───────────────────────────────────────────────────
elems.append(section_banner("2", "ANATOMY"))
elems.append(h2("2.1 Gross Anatomy"))
for b_text in [
"The breast lies between the skin/subdermal adipose tissue and the <b>superficial pectoral fascia</b>, overlying the pectoralis major muscle",
"<b>Cooper's ligaments</b> (suspensory ligaments) run between the chest wall and dermis — infiltration by cancer causes skin <b>dimpling / peau d'orange</b>",
"Three principal tissue types: (1) glandular epithelium, (2) fibrous stroma, (3) adipose tissue",
"<b>15–20 lobes</b>, each ending in a lactiferous duct opening at the nipple; each duct has a dilated lactiferous sinus below the NAC",
"<b>Terminal duct lobular units (TDLUs)</b> = acini + small efferent ductules = the functional milk-forming unit",
"In adolescents: predominant epithelium + stroma; in postmenopausal females: glandular structures largely replaced by adipose tissue",
]:
elems.append(bullet(b_text))
elems.append(Paragraph("<i>Source: Sabiston, Ch. 68</i>", SOURCE))
# Anatomy image
anat_img = os.path.join(IMG_DIR, "anatomy_breast.png")
for el in img(anat_img, W,
"FIGURE 68.1 (Sabiston): Cutaway diagram of a mature resting breast showing Cooper ligaments, "
"lactiferous ducts, TDLU, nipple-areolar complex, pectoralis major, and retromammary fat."):
elems.append(el)
elems.append(h2("2.2 Microanatomy — Terminal Duct Lobular Unit (TDLU)"))
tdlu_img = os.path.join(IMG_DIR, "tdlu_diagram.png")
for el in img(tdlu_img, W*0.65,
"FIGURE 68.2 (Sabiston): The terminal duct lobular unit (TDLU) showing intralobular terminal duct, "
"lobular acini, intralobular stroma, and extralobular stroma."):
elems.append(el)
elems.append(h2("2.3 Lymphatic Drainage"))
lymph_data = [
["Route", "Proportion", "Notes"],
["Axillary nodes (Levels I–III)", "~75%", "Primary drainage from all quadrants"],
["Internal mammary nodes", "~20%", "Medial / central tumours"],
["Rotter's nodes", "Minor", "Between pectoralis major and minor"],
["Supraclavicular nodes", "Advanced", "Skip metastasis or disease progression"],
]
elems.append(make_table(lymph_data, [W*0.38, W*0.18, W*0.44]))
elems.append(Paragraph("<i>Source: Sabiston, Ch. 68; Bailey & Love, Ch. 58</i>", SOURCE))
# ── SECTION 3: RISK FACTORS ───────────────────────────────────────────────
elems.append(section_banner("3", "RISK FACTORS"))
elems.append(Paragraph("Risk factors are divided into modifiable and non-modifiable categories (Table 58.3, Bailey & Love).", BODY))
rf_data = [
["Risk Factor", "Relative Risk / Details"],
["MODIFIABLE", ""],
["Obesity (BMI >30)", "RR = 1.29 in postmenopausal women"],
["Nulliparity / first pregnancy >35 yrs", "Increased oestrogenic exposure"],
["Breastfeeding >12 months", "Protective — greater effect with longer duration"],
["HRT use >10 years", "RR = 1.2"],
["Tobacco: >25 cigarettes/day", "RR = 1.14"],
["Alcohol: light (<1 drink/day)", "RR = 1.05"],
["Alcohol: moderate (3–4 drinks/day)", "RR = 1.32"],
["Alcohol: heavy (>4 drinks/day)", "RR = 1.46"],
["Radiation exposure", "RR = 6"],
["NON-MODIFIABLE", ""],
["Age", "Median presentation ~60 yrs (West), ~48 yrs (Asia)"],
["Early menarche / late menopause", "Prolonged oestrogen exposure"],
["BRCA1 mutation (17q21)", "50–85% lifetime breast cancer risk; 40% ovarian cancer"],
["BRCA2 mutation (13q12.3)", "50–60% lifetime breast cancer risk; 20% ovarian cancer"],
["Prior breast cancer / LCIS / ADH", "Significantly elevated risk"],
["Dense breast tissue", "Higher mammographic density = higher risk"],
["First-degree family history", "~2× relative risk"],
["Previous chest RT (e.g. lymphoma)", "High risk if exposure in adolescence"],
]
# Style sub-headers
rf_ts = 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,0), 9),
("BACKGROUND", (0,1), (-1,1), colors.HexColor("#1a5276")),
("TEXTCOLOR", (0,1), (-1,1), WHITE),
("FONTNAME", (0,1), (-1,1), "Helvetica-Bold"),
("BACKGROUND", (0,11), (-1,11), colors.HexColor("#1a5276")),
("TEXTCOLOR", (0,11), (-1,11), WHITE),
("FONTNAME", (0,11), (-1,11), "Helvetica-Bold"),
("ROWBACKGROUNDS", (0,2), (-1,10), [TABLE_ALT, WHITE]),
("ROWBACKGROUNDS", (0,12), (-1,-1), [TABLE_ALT, WHITE]),
("GRID", (0,0), (-1,-1), 0.5, BORDER),
("FONTNAME", (0,2), (-1,-1), "Helvetica"),
("FONTSIZE", (0,2), (-1,-1), 8.5),
("TEXTCOLOR", (0,2), (-1,-1), GRAY_TEXT),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("SPAN", (0,1), (1,1)),
("SPAN", (0,11),(1,11)),
])
t = Table(rf_data, colWidths=[W*0.55, W*0.45], repeatRows=1)
t.setStyle(rf_ts)
elems.append(t)
elems.append(Paragraph("<i>Source: Bailey & Love, Table 58.3</i>", SOURCE))
# ── SECTION 4: PATHOLOGY ─────────────────────────────────────────────────
elems.append(section_banner("4", "PATHOLOGY"))
elems.append(h2("4.1 Origin of Breast Carcinoma"))
elems.append(body("<b>90%</b> arise from milk ducts (ductal carcinoma) | <b>10%</b> from lobules (lobular carcinoma)"))
for b_text in [
"<b>In situ disease</b>: malignant cells confined within duct/lobule without breaching the basement membrane",
"<b>Invasive (infiltrating) carcinoma</b>: breach of basement membrane with invasion of surrounding tissue",
]:
elems.append(bullet(b_text))
elems.append(h2("4.2 Histological Types"))
hist_data = [
["Histological Type", "Frequency", "Key Features", "Prognosis"],
["Invasive Ductal Carcinoma (IDC / NST)", "50–70%",
"Grows as cohesive mass; discrete on mammogram; palpable lump", "Variable (grade-dependent)"],
["Invasive Lobular Carcinoma (ILC)", "5–15%",
"Single-file infiltration; clinically occult; escapes mammography; CDH1 mutation (loss of E-cadherin)", "Often similar to IDC"],
["Tubular carcinoma", "~2–3%",
"Small glands, single row of bland epithelium; Grade 1", "Excellent"],
["Mucinous / Colloid", "~2–3%",
"Cells floating in copious mucin; Grade 1", "Excellent"],
["Medullary carcinoma", "~5%",
"Bizarre high-grade cells; syncytial sheets; lymphocytic infiltrate; ER/PR/HER2 negative", "Moderate (despite grade 3)"],
["Papillary carcinoma", "0.5–1%",
"Fibrovascular core + epithelial/myoepithelial cells; usually ER+; rarely node-positive", "Good"],
["Metaplastic carcinoma", "<1%",
"High grade; ER/PR/HER2 negative; node-negative but high metastatic potential; ~50% relapse", "Poor"],
]
elems.append(make_table(hist_data, [W*0.26, W*0.1, W*0.42, W*0.22]))
elems.append(h2("4.3 Nottingham Histological Grade (Modified Bloom–Richardson Score)"))
elems.append(body("Three criteria scored 1–3 each:"))
grade_data = [
["Criterion", "Score 1", "Score 2", "Score 3"],
["Tubule/gland formation", ">75% of tumour", "10–75%", "<10%"],
["Nuclear pleomorphism", "Small, regular", "Moderate", "Marked variation"],
["Mitotic rate (per HPF)", "Low", "Intermediate", "High"],
]
elems.append(make_table(grade_data, [W*0.3, W*0.23, W*0.23, W*0.24]))
elems.append(sp(4))
final_data = [
["Total Score", "Grade", "Differentiation"],
["3–5", "Grade I", "Well differentiated"],
["6–7", "Grade II", "Moderately differentiated"],
["8–9", "Grade III", "Poorly differentiated"],
]
elems.append(make_table(final_data, [W*0.25, W*0.25, W*0.50]))
elems.append(h2("4.4 Molecular Classification (PAM-50 / Immunohistochemistry)"))
mol_data = [
["Subtype", "ER/PR Status", "HER2", "Ki-67", "Notes"],
["Luminal A", "Positive", "Negative", "Low", "Best prognosis; low-grade; endocrine-responsive"],
["Luminal B", "Positive", "Negative", "High", "Intermediate prognosis; may need chemo"],
["HER2/neu enriched", "Negative", "Positive", "High", "HER2-targeted therapy needed"],
["Basal (TNBC)", "Negative", "Negative", "Usually high", "Associated with BRCA1; chemotherapy backbone"],
["Claudin-low", "Negative", "Negative", "Variable", "Stem cell features; poor prognosis"],
]
elems.append(make_table(mol_data, [W*0.2, W*0.14, W*0.1, W*0.1, W*0.46]))
elems.append(Paragraph("<i>Source: Bailey & Love, Table 58.4; Sabiston, Ch. 68</i>", SOURCE))
elems.append(h2("4.5 Key Molecular Markers (Sabiston)"))
marker_data = [
["Marker", "Method", "Cut-off / Scoring", "Clinical Use"],
["ER / PR", "IHC", ">1% positive nuclei = positive; 1–10% = low ER", "Endocrine therapy eligibility"],
["HER2", "IHC + ISH", "0/1+ = negative; 3+ = positive; 2+ → ISH confirmation", "HER2-targeted therapy"],
["Ki-67", "IHC", "% positive nuclei (high if >20–30%)", "Proliferation; luminal A vs B distinction"],
["Oncotype DX", "21-gene RT-PCR", "Recurrence score 0–100", "Chemo benefit in ER+/HER2-/node−"],
["MammaPrint", "70-gene array", "Low vs. high risk", "Adjuvant chemo decision (MINDACT trial)"],
]
elems.append(make_table(marker_data, [W*0.15, W*0.15, W*0.35, W*0.35]))
elems.append(h2("4.6 Ductal Carcinoma In Situ (DCIS)"))
for b_text in [
"Malignant cells confined within ducts without invasion of basement membrane",
"Classified by nuclear grade (low/intermediate/high) and necrosis (comedonecrosis = most aggressive subtype)",
"Detected predominantly by screening mammography (<b>microcalcifications</b>)",
"Prevalence of lymphatic/metastatic spread in pure DCIS: <b><1%</b> → systemic chemotherapy NOT required",
"<b>LCIS</b> = high-risk benign lesion, NOT a cancer (AJCC 8th edition); bilateral risk marker",
]:
elems.append(bullet(b_text))
# Histology image
elems.append(h2("4.7 Histopathology — Invasive Ductal Carcinoma"))
hist_img = os.path.join(IMG_DIR, "idc_histology.png")
for el in img(hist_img, W*0.75,
"FIGURE 68.9A (Sabiston): Low-power histology of invasive ductal carcinoma showing nests of "
"malignant ductal cells embedded in desmoplastic fibrous stroma."):
elems.append(el)
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58; Sabiston, Ch. 68</i>", SOURCE))
# ── SECTION 5: CLINICAL FEATURES ─────────────────────────────────────────
elems.append(section_banner("5", "CLINICAL FEATURES & SPREAD"))
elems.append(h2("5.1 Presenting Symptoms"))
for b_text in [
"Painless lump in the breast (most common presentation)",
"New nipple retraction / inversion",
"Blood-stained nipple discharge",
"Skin changes: dimpling, puckering, peau d'orange",
"Change in breast size or asymmetry",
"Axillary lump (nodal metastasis)",
"Bone pain, dyspnoea, jaundice (metastatic disease)",
]:
elems.append(bullet(b_text))
elems.append(h2("5.2 Clinical Signs — Photographs"))
clin_img = os.path.join(IMG_DIR, "inflammatory_ca.png")
for el in img(clin_img, W,
"FIGURE 58.28 (Bailey & Love): (a) Inflammatory carcinoma — diffuse erythema and skin oedema "
"involving >1/3 of the breast with enlarged left breast. (b) Peau d'orange — orange-peel "
"skin appearance indicating locally advanced disease. In darker skin, erythema takes on a brownish hue."):
elems.append(el)
elems.append(h2("5.3 Mechanism of Skin Signs (Bailey & Love)"))
mech_data = [
["Sign", "Mechanism"],
["Skin dimpling", "Single Cooper's ligament shortened by desmoplastic collagen contraction"],
["Puckering / tethering", "Multiple Cooper's ligaments contracted"],
["Nipple retraction", "Central subareolar involvement of Cooper's ligaments"],
["Peau d'orange", "Dermal lymphatic blockage → skin oedema; pores become prominent"],
["Skin ulceration", "Direct tumour invasion through dermis (T4b)"],
["Erythema (inflammatory)", "Dermal lymphatic invasion by tumour emboli; NOT infection"],
]
elems.append(make_table(mech_data, [W*0.35, W*0.65]))
elems.append(body("Tumour releases <b>FGF, TGFα, TGFβ, VEGF</b> → desmoplastic reaction (fibrocytes → fibroblasts → collagen) → contraction of Cooper's ligaments."))
elems.append(h2("5.4 Modes of Spread"))
spread_data = [
["Route", "Details", "Common Sites"],
["Local spread", "Skin → ulceration, satellite nodules; chest wall, pectoralis", "Ipsilateral breast"],
["Lymphatic", "Most common initial route; Level I→II→III axillary nodes; internal mammary", "Axilla (primary)"],
["Haematogenous", "Via intercostal perforators and internal mammary veins",
"Bone (most common) > Lung > Liver > Brain > Adrenal"],
]
elems.append(make_table(spread_data, [W*0.2, W*0.42, W*0.38]))
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58</i>", SOURCE))
# ── SECTION 6: INVESTIGATIONS ─────────────────────────────────────────────
elems.append(section_banner("6", "INVESTIGATIONS — TRIPLE ASSESSMENT"))
elems.append(body("The <b>triple assessment</b> is the gold standard for diagnosis:"))
triple_data = [
["Component", "Methods", "Notes"],
["1. Clinical examination", "History + full breast/axilla examination", "Assess lump characteristics, skin, nodes"],
["2. Imaging", "Mammography ± Ultrasound ± MRI", "See below for details"],
["3. Pathology", "Core needle biopsy (preferred) or FNAC", "Gives histology, grade, ER/PR/HER2"],
]
elems.append(make_table(triple_data, [W*0.22, W*0.38, W*0.40]))
elems.append(h2("6.1 Imaging Modalities"))
img_data = [
["Modality", "Indications", "Features of Malignancy"],
["Mammography (2-view: CC + MLO)",
"Standard screening ≥40 yrs; symptomatic women",
"Spiculate mass, pleomorphic/linear microcalcifications, architectural distortion"],
["Ultrasound",
"Dense breasts, <35 yrs, palpable lumps, guided biopsy",
"Irregular hypoechoic mass; posterior acoustic shadowing; vascularity"],
["MRI Breast",
"BRCA carriers; extent assessment; neoadjuvant response; implants",
"Highest sensitivity; low specificity → guided biopsy needed for MRI-only lesions"],
["PET-CT (18F-FDG)",
"Metastatic work-up, treatment response",
"FDG-avid primary + nodes + distant metastases"],
]
elems.append(make_table(img_data, [W*0.28, W*0.32, W*0.40]))
elems.append(h2("6.2 Staging Investigations"))
for b_text in [
"<b>T3/T4 or N2/N3 disease</b>: CT chest/abdomen/pelvis + isotope bone scan",
"<b>Early cancer (T1/T2, N0/N1)</b>: staging work-up only if symptomatic or raised serum ALP",
"<b>PET-CT</b> may be used as alternative for metastatic work-up",
]:
elems.append(bullet(b_text))
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58</i>", SOURCE))
# ── SECTION 7: STAGING ────────────────────────────────────────────────────
elems.append(section_banner("7", "STAGING — AJCC/UICC TNM 8TH EDITION"))
elems.append(h2("7.1 T (Tumour) Classification"))
t_data = [
["T Category", "Definition"],
["Tis", "In situ (DCIS); LCIS is NOT staged as cancer"],
["T1mi", "Microinvasion ≤1.0 mm"],
["T1a", ">1 mm to ≤5 mm"],
["T1b", ">5 mm to ≤10 mm"],
["T1c", ">10 mm to ≤20 mm"],
["T2", ">20 mm to ≤50 mm"],
["T3", ">50 mm"],
["T4a", "Extension to chest wall (not pectoralis muscle)"],
["T4b", "Ulceration / satellite nodules / peau d'orange (skin involvement)"],
["T4c", "Both T4a and T4b"],
["T4d", "Inflammatory carcinoma"],
]
elems.append(make_table(t_data, [W*0.2, W*0.8]))
elems.append(h2("7.2 N (Node) Classification"))
n_data = [
["N Category", "Definition"],
["N0", "No regional node metastasis"],
["N1", "Movable ipsilateral Level I/II axillary nodes"],
["N2a", "Fixed/matted ipsilateral Level I/II axillary nodes"],
["N2b", "Clinically apparent internal mammary nodes only (no axillary)"],
["N3a", "Ipsilateral infraclavicular (Level III) nodes"],
["N3b", "Ipsilateral internal mammary + axillary nodes"],
["N3c", "Ipsilateral supraclavicular nodes"],
]
elems.append(make_table(n_data, [W*0.15, W*0.85]))
elems.append(h2("7.3 M (Metastasis) Classification"))
m_data = [
["M Category", "Definition"],
["M0", "No clinical or radiographic evidence of distant metastasis"],
["cM0(i+)", "Circulating tumor cells / micrometastasis detected without distant metastasis"],
["M1", "Distant metastasis (bone, lung, liver, brain, distant nodes, etc.)"],
]
elems.append(make_table(m_data, [W*0.2, W*0.8]))
elems.append(h2("7.4 Key Points of 8th Edition AJCC (Bailey & Love — Summary Box 58.3)"))
for b_text in [
"LCIS = high-risk benign lesion — <b>NOT classified as cancer</b>",
"Multiple synchronous tumours: use <b>(m) modifier</b> for T categorisation",
"Post-neoadjuvant therapy status: prefix <b>(y)</b>",
"<b>Pathological complete response (pCR)</b> = absence of tumour cells in breast AND axillary nodes",
"Inflammatory carcinoma remains classified as inflammatory even after complete neoadjuvant remission",
"T1mi = invasive foci <b>≤1.0 mm</b>; tumours >1 mm and <2 mm should be reported as 2 mm",
"8th edition adds: histological grade, ER/PR/HER2/Ki-67, <b>Oncotype DX</b>, neoadjuvant response to refine prognosis",
]:
elems.append(bullet(b_text))
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58; Sabiston, Ch. 68</i>", SOURCE))
# ── SECTION 8: TREATMENT ─────────────────────────────────────────────────
elems.append(section_banner("8", "TREATMENT — MULTIMODAL APPROACH"))
elems.append(body("Treatment is <b>multimodal</b> (surgery + radiotherapy + systemic therapy). "
"All patients should be managed by a <b>multidisciplinary team (MDT)</b>: surgeon, radiologist, "
"pathologist, radiation oncologist, medical oncologist, breast care nurse, reconstructive surgeon."))
elems.append(h2("8.1 Breast-Conserving Surgery (BCS) / Lumpectomy"))
elems.append(body("<i>Synonyms: lumpectomy, partial mastectomy, wide local excision, segmental mastectomy, tylectomy</i>"))
for b_text in [
"Tumour excised with surrounding rim of grossly normal parenchyma; remainder of breast preserved",
"Non-palpable tumours: localization device required (wire, radioactive seed, SAVI Scout, ultrasound-guided)",
"Specimen oriented and inked before sectioning; specimen radiography for non-palpable lesions",
"Clips left in lumpectomy cavity for radiotherapy planning",
"Cavity shave margins at time of lumpectomy reduce positive margin rates (Level I evidence)",
]:
elems.append(bullet(b_text))
elems.append(h3("Margin Standards (SSO/ASTRO/ASCO Consensus — Sabiston):"))
margin_data = [
["Cancer Type", "Required Negative Margin", "Evidence Base"],
["Invasive breast cancer", '"No ink on tumour"',
"Meta-analysis, 28,162 patients; wider margins do NOT further reduce recurrence"],
["DCIS", "2 mm",
"Meta-analysis, 7,883 patients; 2 mm superior to 0–1 mm margins"],
]
elems.append(make_table(margin_data, [W*0.28, W*0.28, W*0.44]))
elems.append(body("Positive margins = <b>2-fold increase</b> in ipsilateral breast tumour recurrence risk."))
elems.append(h2("8.2 Mastectomy — Types and Indications"))
mast_data = [
["Type", "Description", "Main Indication"],
["Simple / Total mastectomy", "All breast tissue + NAC; no axillary dissection", "Prophylactic; DCIS; with SLNB"],
["Modified radical mastectomy (MRM)", "Simple mastectomy + Level I/II ALND; pectoralis preserved", "Node-positive invasive cancer"],
["Skin-sparing mastectomy", "Preserves skin envelope; removes NAC", "For immediate reconstruction"],
["Nipple-sparing mastectomy", "Preserves entire NAC", "BRCA prophylactic; selected small tumours away from NAC"],
]
elems.append(make_table(mast_data, [W*0.28, W*0.38, W*0.34]))
elems.append(h3("Indications for mastectomy over BCS:"))
for b_text in [
"Multicentric disease (multiple quadrants)",
"Large tumour-to-breast ratio with poor cosmesis",
"Prior radiotherapy to the ipsilateral breast",
"Positive margins after re-excision",
"BRCA mutation carriers (risk-reducing surgery)",
"Patient preference",
]:
elems.append(bullet(b_text))
elems.append(h2("8.3 Axillary Management"))
elems.append(h3("Sentinel Lymph Node Biopsy (SLNB):"))
for b_text in [
"Standard for clinically node-negative disease; uses blue dye ± radioisotope (Tc-99m)",
"If SLN negative: no further axillary treatment required",
"<b>ACOSOG Z0011 trial</b>: 1–2 positive SLNs in BCS + whole-breast RT → ALND NOT required",
]:
elems.append(bullet(b_text))
elems.append(h3("Axillary Lymph Node Dissection (ALND):"))
for b_text in [
"Level I and II nodes removed (typically ≥10 nodes)",
"Indications: positive SLN (selected), clinically positive nodes",
"Complications: lymphoedema (up to 20%), shoulder stiffness, sensory loss, seroma",
]:
elems.append(bullet(b_text))
elems.append(h2("8.4 Radiotherapy"))
rt_data = [
["Setting", "Regimen", "Indication"],
["After BCS (adjuvant)", "Standard: 50 Gy/25 fractions OR Hypofractionation: 40 Gy/15 fractions",
"All patients after BCS; hypofractionation now preferred"],
["Partial breast irradiation", "Various techniques (APBI, brachytherapy)",
"Low-risk patients; avoids whole breast radiation"],
["Post-mastectomy RT (PMRT)", "Chest wall ± nodal basins",
"T3/T4; ≥4 positive nodes; positive margins; N3 disease"],
["Nodal RT", "Axillary/supraclavicular/internal mammary",
"High-risk patients with nodal involvement"],
]
elems.append(make_table(rt_data, [W*0.22, W*0.42, W*0.36]))
elems.append(body("Adjuvant RT reduces locoregional recurrence and improves breast cancer-related mortality by eradicating residual occult disease."))
elems.append(h2("8.5 Systemic Therapy"))
elems.append(h3("A. Chemotherapy"))
chemo_data = [
["Regimen", "Drugs", "Use"],
["AC → T", "Doxorubicin + Cyclophosphamide → Paclitaxel/Docetaxel", "Standard adjuvant for high-risk"],
["TC", "Docetaxel + Cyclophosphamide", "Lower-risk HER2-negative"],
["AC → TH (+ P)", "AC → Taxane + Trastuzumab (+ Pertuzumab)", "HER2-positive (neoadjuvant/adjuvant)"],
["Capecitabine", "Oral fluoropyrimidine", "Residual TNBC after NACT (CREATE-X trial)"],
["T-DM1", "Ado-trastuzumab emtansine (antibody-drug conjugate)", "Residual HER2+ disease after NACT"],
]
elems.append(make_table(chemo_data, [W*0.2, W*0.44, W*0.36]))
elems.append(h3("B. Endocrine Therapy"))
endo_data = [
["Agent", "Patients", "Duration", "Key Effects"],
["Tamoxifen (SERM)", "ER+ premenopausal", "5–10 years",
"Reduces recurrence ~40%; contralateral BC ↓47%; risks: endometrial cancer, DVT/PE"],
["Aromatase Inhibitors (anastrozole, letrozole, exemestane)",
"ER+ postmenopausal", "5 years (or switch after tamoxifen)",
"Superior to tamoxifen in postmenopausal women; risks: osteoporosis, arthralgia"],
["GnRH agonist + AI", "High-risk premenopausal", "≥5 years", "For premenopausal women with high-risk ER+ cancer (SOFT/TEXT trials)"],
["CDK 4/6 inhibitors (palbociclib, ribociclib, abemaciclib)",
"Advanced/metastatic ER+/HER2−", "Until progression", "Significantly improves PFS in metastatic; abemaciclib approved adjuvant high-risk"],
]
elems.append(make_table(endo_data, [W*0.25, W*0.2, W*0.15, W*0.40]))
elems.append(h3("C. HER2-Targeted Therapy"))
her2_data = [
["Agent", "Mechanism", "Use"],
["Trastuzumab (Herceptin)", "Anti-HER2 monoclonal antibody", "1 year adjuvant; reduces recurrence ~50% in early HER2+ BC"],
["Pertuzumab", "HER2 dimerization inhibitor", "Combined with trastuzumab as dual blockade (neoadjuvant/adjuvant)"],
["T-DM1 (ado-trastuzumab emtansine)", "Antibody-drug conjugate", "Residual invasive HER2+ disease after NACT"],
["Lapatinib / Tucatinib / Neratinib", "Tyrosine kinase inhibitors", "Advanced/metastatic HER2+ disease"],
]
elems.append(make_table(her2_data, [W*0.3, W*0.3, W*0.4]))
elems.append(h3("D. Chemoprevention (Sabiston, Ch. 68)"))
prev_data = [
["Agent", "Population", "Trial", "Risk Reduction"],
["Tamoxifen 20 mg/day × 5 yrs",
"High-risk pre/postmenopausal (Gail score ≥1.7%, LCIS, ADH/ALH)",
"NSABP P-1",
"43% invasive BC; 59% in LCIS; 75% in ADH/ALH"],
["Raloxifene", "Postmenopausal women", "STAR trial",
"Similar to tamoxifen; fewer uterine side effects"],
["Anastrozole", "Postmenopausal high-risk women", "IBIS-II",
"~50% reduction"],
["Exemestane", "Postmenopausal high-risk women", "MAP.3",
"65% reduction"],
]
elems.append(make_table(prev_data, [W*0.28, W*0.28, W*0.14, W*0.30]))
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58; Sabiston, Ch. 68</i>", SOURCE))
# ── SECTION 9: SPECIAL SCENARIOS ─────────────────────────────────────────
elems.append(section_banner("9", "SPECIAL SCENARIOS"))
elems.append(h2("9.1 Hereditary & Familial Breast Cancer (Bailey & Love)"))
for b_text in [
"<b>Hereditary breast cancer (HBC)</b>: 5–10% of all breast cancers; identifiable genetic mutation; more aggressive, earlier onset, multicentric, bilateral",
"<b>Familial breast cancer (FBC)</b>: 20–30%; family clustering without identified mutation",
"<b>Sporadic</b>: ~70% of all breast cancers",
]:
elems.append(bullet(b_text))
gene_data = [
["Gene", "Chromosome", "Lifetime Breast Cancer Risk", "Other Cancers", "Tumour Subtype"],
["BRCA1", "17q21", "50–85%", "Ovarian (40%)", "Mostly TNBC"],
["BRCA2", "13q12.3", "50–60%", "Ovarian (20%), prostate, colon, pancreas", "ER+ (usually)"],
["TP53", "17p13.1", "~50%", "Li-Fraumeni syndrome (sarcoma, brain, adrenal)", "Variable"],
["PTEN", "10q23.3", "25–50%", "Cowden syndrome (thyroid, endometrium)", "Variable"],
["STK11", "19p13.3", "~50%", "Peutz-Jeghers syndrome (GI polyposis)", "Variable"],
["CDH1", "16q22.1", "39–52%", "Diffuse gastric cancer", "Lobular (ILC)"],
]
elems.append(make_table(gene_data, [W*0.12, W*0.15, W*0.22, W*0.28, W*0.23]))
elems.append(h3("Management of BRCA Mutation Carriers:"))
for b_text in [
"<b>Bilateral risk-reducing mastectomy + immediate reconstruction</b>: reduces breast cancer risk by <b>90%</b>",
"<b>Chemoprophylaxis</b> (tamoxifen or anastrozole): reduces risk by <b>50%</b>",
"<b>Bilateral salpingo-oophorectomy (BSO)</b>: after family completion at ~35–40 years",
"Annual MRI + mammography from age 30 for surveillance",
]:
elems.append(bullet(b_text))
elems.append(h2("9.2 Breast Cancer in Pregnancy"))
for b_text in [
"Associated with aggressive tumour biology, particularly TNBC",
"Imaging: ultrasound first; mammogram with abdominal shielding; MRI without gadolinium preferred",
"Chemotherapy: generally safe <b>after 1st trimester</b>; avoid anthracyclines late in pregnancy",
"<b>Trastuzumab: contraindicated</b> in pregnancy (fetal renal toxicity)",
"Surgery can be performed in all trimesters",
"Termination of pregnancy does NOT improve prognosis",
]:
elems.append(bullet(b_text))
elems.append(h2("9.3 Inflammatory Breast Cancer"))
for b_text in [
"Clinical diagnosis: erythema + oedema involving <b>>1/3 of the breast</b>",
"Classified as <b>T4d</b> regardless of tumour size",
"Caused by <b>dermal lymphatic invasion</b> by tumour emboli — not infection",
"<b>Treatment: NACT first</b> → MRM + post-mastectomy radiation (NOT upfront surgery alone)",
"Remains classified as inflammatory carcinoma even after complete neoadjuvant remission (AJCC 8th)",
]:
elems.append(bullet(b_text))
elems.append(h2("9.4 Male Breast Cancer"))
for b_text in [
"Accounts for <b><1%</b> of all breast cancers",
"BRCA2 mutation more common than BRCA1 in male breast cancer",
"Mostly invasive ductal, ER positive",
"Mastectomy preferred over BCS (small breast volume)",
"Tamoxifen for hormonal therapy; AIs require concurrent GnRH agonist",
]:
elems.append(bullet(b_text))
elems.append(h2("9.5 DCIS Management (Sabiston)"))
dcis_data = [
["Treatment Option", "Key Evidence / Notes"],
["BCS + adjuvant RT", "Standard; 2 mm negative margin required"],
["Mastectomy", "For multicentric DCIS, large DCIS, patient preference"],
["Tamoxifen (ER+ DCIS, premenopausal)", "NSABP B-24: reduces ipsilateral recurrence (16.6% → 13.2%), contralateral BC ↓40%"],
["Anastrozole (ER+ DCIS, postmenopausal <60 yrs)", "NSABP B-35: anastrozole superior to tamoxifen in breast cancer-free survival"],
["Adjuvant endocrine therapy overall", "Reduces DCIS recurrence risk by ~50%"],
]
elems.append(make_table(dcis_data, [W*0.4, W*0.6]))
elems.append(h2("9.6 Local Recurrence & Metastatic Disease (Bailey & Love)"))
elems.append(h3("Local Recurrence:"))
for b_text in [
"Biopsy first — receptor status may change and influence therapy",
"Whole-body MRI or PET-CT to exclude distant metastasis",
"Systemic chemotherapy followed by surgical excision",
"Most surgeons perform mastectomy; second BCS + re-RT may be considered in selected cases",
]:
elems.append(bullet(b_text))
elems.append(h3("Metastatic Disease:"))
for b_text in [
"Bony metastasis: palliative RT to weight-bearing lesions + <b>bisphosphonates/denosumab</b>",
"Symptomatic pleural effusions: chest drainage + pleurodesis",
"Surgical resection of <b>solitary visceral metastasis</b> in good performance status",
"Systemic therapy: endocrine ± CDK4/6i (ER+), HER2 therapy (HER2+), chemotherapy, PARP inhibitors (BRCA mutation carriers)",
]:
elems.append(bullet(b_text))
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58; Sabiston, Ch. 68</i>", SOURCE))
# ── SECTION 10: RECONSTRUCTION ───────────────────────────────────────────
elems.append(section_banner("10", "BREAST RECONSTRUCTION"))
rec_data = [
["Technique", "Description", "Advantages / Notes"],
["Tissue expander → implant", "Two-stage implant-based; expander placed, later exchanged for permanent implant ± ADM",
"Simpler surgery; no donor site; may need revision"],
["Direct-to-implant", "Single-stage implant placement with ADM",
"One operation; suitable for smaller breasts"],
["TRAM flap (pedicled/free)", "Transverse rectus abdominis myocutaneous flap from abdomen",
"Autologous; natural feel; sacricifes rectus muscle → hernia risk"],
["DIEP flap (free)", "Deep inferior epigastric perforator flap; no muscle sacrifice",
"Gold standard autologous; requires microsurgery; best donor site aesthetics"],
["LD flap", "Latissimus dorsi pedicled flap ± implant",
"Reliable; well-vascularised; often used after RT; smaller volume"],
["SGAP / IGAP", "Superior/inferior gluteal artery perforator free flap",
"Used when abdominal tissue unavailable; complex microsurgery"],
]
elems.append(make_table(rec_data, [W*0.22, W*0.42, W*0.36]))
elems.append(body("<b>Timing:</b> Immediate reconstruction (same operation as mastectomy — better psychological outcomes) "
"vs. delayed (after adjuvant therapy — safer when post-mastectomy RT is planned)."))
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58</i>", SOURCE))
# ── SECTION 11: SCREENING ─────────────────────────────────────────────────
elems.append(section_banner("11", "SCREENING"))
screen_data = [
["Programme", "Age Range", "Frequency", "Modality"],
["UK NHS Breast Screening", "50–70 yrs (expanding 47–73)", "Every 3 years", "2-view mammography"],
["American Cancer Society (ACS)", "Annual from 40–45 yrs; biennial 55+", "Annual / biennial", "Mammography"],
["USPSTF", "50–74 yrs", "Every 2 years", "Mammography"],
["High-risk (BRCA/family hx)", "From age 30", "Annual", "MRI + mammography"],
]
elems.append(make_table(screen_data, [W*0.32, W*0.25, W*0.18, W*0.25]))
elems.append(body("Screening reduces breast cancer mortality by approximately <b>30–35%</b> in the screened population. "
"Down-staging at detection is the primary mechanism of benefit."))
# ── SECTION 12: PROGNOSIS ─────────────────────────────────────────────────
elems.append(section_banner("12", "PROGNOSIS & SURVIVAL"))
elems.append(h2("12.1 Prognostic Factors"))
prog_data = [
["Factor", "Significance"],
["Axillary node status", "Most powerful single prognostic factor overall"],
["Tumour size (T stage)", "Most important anatomical factor"],
["Histological grade", "Grade III = significantly worse prognosis"],
["ER/PR status", "ER+ = better prognosis; endocrine therapy responsive"],
["HER2 status", "HER2+ = worse prognosis without targeted therapy; HER2 therapy reverses this"],
["Ki-67 / proliferation index", "High Ki-67 = higher recurrence risk"],
["Lymphovascular invasion (LVI)", "Independent adverse prognostic factor"],
["pCR after neoadjuvant therapy", "Excellent prognostic marker, especially in HER2+ and TNBC"],
["Oncotype DX recurrence score", "Guides chemotherapy need in ER+/HER2-/node- (TAILORx trial)"],
["MammaPrint", "Low vs. high risk for distant recurrence (MINDACT trial)"],
]
elems.append(make_table(prog_data, [W*0.35, W*0.65]))
elems.append(h2("12.2 5-Year Overall Survival by Stage"))
surv_data = [
["Stage", "Approximate 5-Year Overall Survival"],
["Stage I (T1N0M0)", ">95%"],
["Stage IIA (T2N0 or T1N1)", "~85–90%"],
["Stage IIB (T2N1 or T3N0)", "~70–80%"],
["Stage IIIA–C (locally advanced)", "~50–70%"],
["Stage IV (metastatic)", "~25–30%"],
]
surv_ts = 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,0), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1),
[colors.HexColor("#e8f5e9"), colors.HexColor("#fff9c4"),
colors.HexColor("#fff9c4"), colors.HexColor("#ffe0b2"),
colors.HexColor("#ffcdd2")]),
("GRID", (0,0), (-1,-1), 0.5, BORDER),
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 9),
("TEXTCOLOR", (0,1), (-1,-1), GRAY_TEXT),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("ALIGN", (1,0), (1,-1), "CENTER"),
])
t = Table(surv_data, colWidths=[W*0.45, W*0.55])
t.setStyle(surv_ts)
elems.append(t)
elems.append(Paragraph("<i>Source: Bailey & Love, Ch. 58; Sabiston, Ch. 68</i>", SOURCE))
# ── FOOTER NOTE ──────────────────────────────────────────────────────────
elems.append(sp(12))
elems.append(hr())
elems.append(Paragraph(
"<b>Sources:</b> Bailey & Love's Short Practice of Surgery, 28th Edition — Chapter 58: The Breast | "
"Sabiston Textbook of Surgery: The Biological Basis of Modern Surgical Practice — Chapter 68: Diseases of the Breast<br/>"
"<b>Recent Evidence Note (PubMed 2024–2026):</b> Baker et al. (Breast, Dec 2024; PMID 39270543) — systematic review confirming "
"that atypical ductal/lobular hyperplasia, LCIS, and flat epithelial atypia significantly elevate future breast cancer risk, "
"consistent with current chemoprevention and surveillance protocols.",
NOTE))
return elems
# ── Page template with header/footer ─────────────────────────────────────────
def on_page(canvas, doc):
canvas.saveState()
w, h = A4
# Header bar
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, h - 1.8*cm, w, 1.8*cm, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 10)
canvas.setFillColor(WHITE)
canvas.drawString(1.5*cm, h - 1.2*cm, "BREAST CANCER — Comprehensive Surgical Reference")
canvas.setFont("Helvetica", 8)
canvas.drawRightString(w - 1.5*cm, h - 1.2*cm, "Bailey & Love 28th Ed. | Sabiston")
# Footer
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, 0, w, 1.2*cm, fill=1, stroke=0)
canvas.setFont("Helvetica", 8)
canvas.setFillColor(WHITE)
canvas.drawString(1.5*cm, 0.45*cm, "© Compiled from standard surgical textbooks — for educational use")
canvas.drawRightString(w - 1.5*cm, 0.45*cm, f"Page {doc.page}")
canvas.restoreState()
def on_first_page(canvas, doc):
canvas.saveState()
w, h = A4
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, 0, w, h, fill=1, stroke=0)
canvas.restoreState()
# ── Build PDF ────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.2*cm, bottomMargin=1.8*cm,
title="Breast Cancer Comprehensive Reference",
author="Bailey & Love 28th Ed. + Sabiston",
subject="Breast Cancer Surgery"
)
story = []
story += cover_elements(A4[0], A4[1])
story += build_content()
doc.build(story, onFirstPage=on_first_page, onLaterPages=on_page)
print(f"\nPDF created: {OUTPUT}")
cd /home/daytona/workspace/breast-cancer-pdf && python generate_pdf.py 2>&1
~/breast-cancer-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/breast-cancer-pdf/generate_pdf.py'
cd /home/daytona/workspace/breast-cancer-pdf && python generate_pdf.py 2>&1
~/breast-cancer-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/breast-cancer-pdf/generate_pdf.py'
cd /home/daytona/workspace/breast-cancer-pdf && python generate_pdf.py 2>&1
ls -lh /home/daytona/workspace/breast-cancer-pdf/Breast_Cancer_Comprehensive_Reference.pdf
Breast Cancer Comprehensive Reference
PDF Document · PDF