~/surgical-triads/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame
from reportlab.lib.colors import HexColor
import datetime
OUTPUT = "/home/daytona/workspace/surgical-triads/Surgical_Triads_Summary.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
C_NAVY = HexColor("#1B3A5C")
C_TEAL = HexColor("#1A7E8C")
C_LIGHT = HexColor("#E8F4F8")
C_HEADER = HexColor("#2C5F7A")
C_ALT = HexColor("#F0F7FA")
C_WHITE = colors.white
C_BLACK = colors.black
C_BORDER = HexColor("#A0C8D8")
C_WARN = HexColor("#D4580A")
C_SECTION = [
HexColor("#1B5E8C"), # Hepatobiliary
HexColor("#1A7A5E"), # Trauma
HexColor("#6B3FA0"), # Gynaecology
HexColor("#8C3A1A"), # ENT
HexColor("#2A5C8C"), # Orthopaedics
HexColor("#4A6741"), # Neurosurgery
HexColor("#8C6B1A"), # Cardiovascular
HexColor("#5A3A7A"), # Metabolic
]
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm,
title="Surgical Triads – Comparative Summary",
author="Orris Medical"
)
styles = getSampleStyleSheet()
W = A4[0] - 4*cm # usable width
def style(name, **kw):
s = ParagraphStyle(name, parent=styles["Normal"], **kw)
return s
S_TITLE = style("DocTitle", fontSize=26, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER, leading=32)
S_SUBTITLE= style("DocSub", fontSize=13, textColor=HexColor("#D0E8F0"),
fontName="Helvetica", alignment=TA_CENTER, leading=18)
S_DATE = style("DocDate", fontSize=9, textColor=HexColor("#A0C8D8"),
fontName="Helvetica", alignment=TA_CENTER)
S_SECHEAD = style("SecHead", fontSize=14, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_LEFT, leading=18)
S_TRINAME = style("TriName", fontSize=11, textColor=C_NAVY,
fontName="Helvetica-Bold", leading=14)
S_BODY = style("Body", fontSize=9, textColor=C_BLACK,
fontName="Helvetica", leading=13)
S_COMP = style("Comp", fontSize=9, textColor=C_BLACK,
fontName="Helvetica", leading=12)
S_BOLD = style("Bold", fontSize=9, textColor=C_NAVY,
fontName="Helvetica-Bold", leading=12)
S_SMALL = style("Small", fontSize=8, textColor=HexColor("#555555"),
fontName="Helvetica-Oblique", leading=11)
S_NOTE = style("Note", fontSize=8, textColor=C_WARN,
fontName="Helvetica-Bold", leading=11)
S_FOOT = style("Foot", fontSize=7.5,textColor=HexColor("#888888"),
fontName="Helvetica", alignment=TA_CENTER)
S_TOC_ITEM= style("TOCItem", fontSize=10, textColor=C_NAVY,
fontName="Helvetica", leading=15, leftIndent=0.5*cm)
def section_color(idx):
return C_SECTION[idx % len(C_SECTION)]
# ── Data ─────────────────────────────────────────────────────────────────────
SYSTEMS = [
{
"system": "Hepatobiliary & GI Surgery",
"icon": "🏥",
"color_idx": 0,
"triads": [
{
"name": "Charcot's Triad",
"condition": "Acute Cholangitis",
"components": [
("Fever + rigors", "Bacteraemia from biliary obstruction"),
("Jaundice", "Obstruction of common bile duct"),
("Right upper quadrant pain", "Biliary distension & inflammation"),
],
"note": "Present in ~70% of cholangitis. Requires IV antibiotics + urgent biliary drainage (ERCP).",
"extension": "Reynolds' Pentad adds shock + altered consciousness → suppurative cholangitis (high mortality).",
},
{
"name": "Whipple's Triad",
"condition": "Insulinoma",
"components": [
("Symptoms of hypoglycaemia", "Sweating, tremor, palpitations, confusion"),
("Low plasma glucose", "<45–50 mg/dL during episode"),
("Relief with glucose", "Symptoms resolve on glucose administration"),
],
"note": "Pathognomonic for insulinoma (functional β-cell tumour). Confirm with 72-h supervised fast.",
"extension": None,
},
{
"name": "Saint's Triad",
"condition": "Co-existing GI Pathologies",
"components": [
("Gallstones (cholelithiasis)", "Biliary calculi"),
("Hiatus hernia", "GEJ herniation through diaphragm"),
("Colonic diverticulosis", "Diverticula of large bowel"),
],
"note": "All three are common in elderly, obese, Western patients. Do not attribute all symptoms to one alone.",
"extension": None,
},
{
"name": "Mackler's Triad",
"condition": "Oesophageal Perforation (Boerhaave Syndrome)",
"components": [
("Vomiting", "Forceful retching causes barotrauma"),
("Chest pain", "Sudden, severe, tearing chest/epigastric pain"),
("Subcutaneous emphysema", "Air tracks into neck/chest wall tissues"),
],
"note": "Surgical emergency. High mortality if delayed. Needs immediate surgical repair + IV antibiotics.",
"extension": None,
},
{
"name": "Carcinoid Triad",
"condition": "Carcinoid Syndrome (with Hepatic Metastases)",
"components": [
("Flushing", "Episodic cutaneous flushing of face/neck/trunk"),
("Diarrhoea", "Secretory, watery diarrhoea"),
("Bronchospasm", "Wheezing from serotonin/kinin release"),
],
"note": "Occurs only when hepatic metastases bypass liver metabolism. Right heart carcinoid disease is a major complication. Treat with octreotide.",
"extension": None,
},
]
},
{
"system": "Trauma Surgery",
"icon": "⚡",
"color_idx": 1,
"triads": [
{
"name": "The Lethal Triad",
"condition": "Damage Control Surgery",
"components": [
("Hypothermia", "Core temp <35°C; impairs coagulation enzyme function"),
("Acidosis", "Metabolic lactic acidosis from shock & hypoperfusion"),
("Coagulopathy", "Dilutional + consumptive from massive haemorrhage"),
],
"note": "Each element worsens the others (vicious cycle). Indication for Damage Control Surgery: abbreviated surgery → ICU resuscitation → definitive repair.",
"extension": None,
},
]
},
{
"system": "Cardiovascular Surgery",
"icon": "❤️",
"color_idx": 6,
"triads": [
{
"name": "Beck's Triad (Acute)",
"condition": "Cardiac Tamponade",
"components": [
("Hypotension", "Reduced cardiac output from pericardial compression"),
("Raised JVP / Distended neck veins", "Impaired venous return to heart"),
("Muffled / distant heart sounds", "Pericardial fluid dampens sounds"),
],
"note": "Surgical emergency: pericardiocentesis or pericardial window. Additional finding: pulsus paradoxus >10 mmHg.",
"extension": "Beck's chronic triad: increased CVP + ascites + small quiet heart.",
},
{
"name": "Virchow's Triad",
"condition": "Venous Thrombosis (DVT / PE)",
"components": [
("Endothelial injury", "Surgery, trauma, inflammation, toxins"),
("Stasis / abnormal blood flow", "Immobility, heart failure, post-op state"),
("Hypercoagulability", "Inherited or acquired (malignancy, OCP, pregnancy)"),
],
"note": "All three factors co-exist in surgical patients. Forms the basis for VTE prophylaxis (LMWH, compression, early mobilisation).",
"extension": None,
},
]
},
{
"system": "Gynaecological Surgery",
"icon": "🔬",
"color_idx": 2,
"triads": [
{
"name": "Meigs' Syndrome",
"condition": "Benign Ovarian Fibroma",
"components": [
("Ovarian fibroma / fibrothecoma", "Benign solid ovarian tumour"),
("Ascites", "Transudative peritoneal fluid"),
("Pleural effusion", "Usually right-sided transudate"),
],
"note": "All features resolve completely after surgical tumour removal. Must be distinguished from ovarian malignancy (CA-125 may be mildly elevated).",
"extension": None,
},
]
},
{
"system": "ENT / Head & Neck Surgery",
"icon": "👂",
"color_idx": 3,
"triads": [
{
"name": "Ménière's Disease Triad",
"condition": "Endolymphatic Hydrops",
"components": [
("Sensorineural hearing loss", "Low-frequency, fluctuating"),
("Tinnitus", "Low-pitched roaring tinnitus"),
("Episodic vertigo", "Attacks lasting 20 min – 24 hours"),
],
"note": "Also described as a tetrad with aural fullness. Vertigo is the most disabling. Treat with salt restriction, betahistine, diuretics, intratympanic steroids.",
"extension": None,
},
{
"name": "Gradenigo's Triad",
"condition": "Petrous Apicitis",
"components": [
("Persistent otorrhoea", "From chronic/acute otitis media"),
("Retro-orbital / deep facial pain", "Trigeminal nerve (CN V) involvement"),
("Ipsilateral lateral rectus palsy", "Abducens nerve (CN VI) involvement"),
],
"note": "Complication of otitis media spreading to petrous apex. Needs mastoidectomy ± petrous apex drainage + IV antibiotics.",
"extension": None,
},
{
"name": "Samter's Triad (AERD)",
"condition": "Aspirin-Exacerbated Respiratory Disease",
"components": [
("Bronchial asthma", "Chronic, often severe"),
("Nasal polyposis", "Recurrent nasal polyps"),
("Aspirin / NSAID sensitivity", "Dramatic bronchoconstriction on exposure"),
],
"note": "COX-1 inhibition → leukotriene excess → bronchospasm. Treatment: FESS for polyps; aspirin desensitisation post-op. Avoid all COX-1 NSAIDs.",
"extension": None,
},
]
},
{
"system": "Orthopaedic Surgery",
"icon": "🦴",
"color_idx": 4,
"triads": [
{
"name": "O'Donoghue's Unhappy Triad",
"condition": "Knee Valgus Injury",
"components": [
("Medial collateral ligament (MCL) tear", "Valgus stress component"),
("Anterior cruciate ligament (ACL) tear", "Rotational injury component"),
("Medial meniscus tear", "Entrapped between femur and tibia"),
],
"note": "Classic injury from valgus + external rotation force on planted foot. ACL reconstruction ± meniscal repair; MCL usually heals conservatively.",
"extension": None,
},
{
"name": "Terrible Triad of the Elbow",
"condition": "Elbow Instability",
"components": [
("Elbow dislocation", "Posterior dislocation most common"),
("Radial head fracture", "Mason classification"),
("Coronoid process fracture", "Regan-Morrey classification"),
],
"note": "'Terrible' because all three stabilisers are disrupted. ORIF coronoid + ORIF/arthroplasty radial head + lateral ligament repair. Early motion essential.",
"extension": None,
},
]
},
{
"system": "Neurosurgery",
"icon": "🧠",
"color_idx": 5,
"triads": [
{
"name": "Cushing's Triad",
"condition": "Raised Intracranial Pressure / Brainstem Herniation",
"components": [
("Hypertension (widened pulse pressure)", "Cushing reflex to maintain CPP"),
("Bradycardia", "Vagal response to hypertension"),
("Irregular / slow respirations", "Brainstem compression of respiratory centres"),
],
"note": "Late, ominous sign – impending herniation. Emergency: urgent CT + ICP management (mannitol, hyperventilation, decompressive craniectomy).",
"extension": None,
},
{
"name": "Wernicke's Triad",
"condition": "Wernicke's Encephalopathy (Thiamine Deficiency)",
"components": [
("Ophthalmoplegia ± nystagmus", "CN VI palsy; external ophthalmoplegia"),
("Cerebellar ataxia", "Gait ataxia predominantly"),
("Global confusion", "Acute confusional state, disorientation"),
],
"note": "Full triad in only 1/3 of cases – treat without waiting. Give IV thiamine BEFORE glucose. Risk: alcoholism, post-bariatric surgery, prolonged vomiting.",
"extension": None,
},
{
"name": "Hakim's Triad (NPH)",
"condition": "Normal Pressure Hydrocephalus",
"components": [
("Gait disturbance", "Magnetic gait – shuffling, broad-based ('feet stuck')"),
("Urinary incontinence", "Urgency incontinence"),
("Cognitive impairment", "Subcortical dementia, slowed thinking"),
],
"note": "Mnemonic: 'Wet, Wobbly, Wacky.' Treatable dementia – ventriculoperitoneal shunting reverses symptoms (gait responds best).",
"extension": None,
},
]
},
{
"system": "Metabolic / Endocrine (Surgical Relevance)",
"icon": "⚗️",
"color_idx": 7,
"triads": [
{
"name": "Charcot's Neuroarthropathy Triad",
"condition": "Diabetic Foot (Charcot Foot)",
"components": [
("Peripheral neuropathy", "Loss of protective sensation"),
("Trauma / repetitive microtrauma", "Unperceived injury to the foot"),
("Adequate blood supply", "Paradoxically preserved vascularity"),
],
"note": "Progressive destruction of foot joints. Acute phase: warmth, swelling, erythema. Managed with offloading; surgical reconstruction in late disease.",
"extension": None,
},
]
},
]
# ── Build PDF ─────────────────────────────────────────────────────────────────
story = []
# ── COVER PAGE ────────────────────────────────────────────────────────────────
# Cover background block (simulated with a large coloured Table)
cover_data = [[Paragraph("SURGICAL TRIADS", S_TITLE)]]
cover_tbl = Table(cover_data, colWidths=[W])
cover_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 24),
("BOTTOMPADDING",(0,0),(-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING",(0,0), (-1,-1), 12),
("ROUNDEDCORNERS",(0,0),(-1,-1), [8,8,0,0]),
]))
story.append(cover_tbl)
sub_data = [[Paragraph("A Comparative Summary by Organ System", S_SUBTITLE)]]
sub_tbl = Table(sub_data, colWidths=[W])
sub_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_TEAL),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING",(0,0),(-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING",(0,0), (-1,-1), 12),
("ROUNDEDCORNERS",(0,0),(-1,-1), [0,0,8,8]),
]))
story.append(sub_tbl)
story.append(Spacer(1, 0.4*cm))
date_str = datetime.datetime.now().strftime("%B %Y")
story.append(Paragraph(f"Prepared by Orris Medical | {date_str}", S_DATE))
story.append(Spacer(1, 1*cm))
# ── QUICK REFERENCE TABLE ──────────────────────────────────────────────────
qr_header = [
Paragraph("TRIAD", style("QRH", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER)),
Paragraph("CONDITION", style("QRH2", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER)),
Paragraph("KEY COMPONENTS", style("QRH3", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER)),
Paragraph("SYSTEM", style("QRH4", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER)),
]
qr_rows = [qr_header]
alt = False
for sys_data in SYSTEMS:
col = section_color(sys_data["color_idx"])
for tri in sys_data["triads"]:
comps_short = " | ".join(c[0] for c in tri["components"][:3])
bg = C_ALT if alt else C_WHITE
row = [
Paragraph(tri["name"], style(f"QRN{alt}", fontSize=8.5,
fontName="Helvetica-Bold", textColor=C_NAVY)),
Paragraph(tri["condition"], style(f"QRC{alt}", fontSize=8.5,
fontName="Helvetica", textColor=C_BLACK)),
Paragraph(comps_short, style(f"QRK{alt}", fontSize=7.5,
fontName="Helvetica-Oblique", textColor=HexColor("#444444"))),
Paragraph(sys_data["system"], style(f"QRS{alt}", fontSize=8,
fontName="Helvetica", textColor=col)),
]
qr_rows.append(row)
alt = not alt
qr_tbl = Table(qr_rows, colWidths=[W*0.21, W*0.25, W*0.33, W*0.21])
qr_style = [
("BACKGROUND", (0,0), (-1,0), C_HEADER),
("ROWBACKGROUNDS",(0,1),(-1,-1), [C_WHITE, C_ALT]),
("GRID", (0,0), (-1,-1), 0.3, C_BORDER),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
qr_tbl.setStyle(TableStyle(qr_style))
# Section header for QR table
qr_head_data = [[Paragraph("QUICK REFERENCE — ALL TRIADS AT A GLANCE", S_SECHEAD)]]
qr_head_tbl = Table(qr_head_data, colWidths=[W])
qr_head_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_HEADER),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING",(0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("ROUNDEDCORNERS",(0,0),(-1,-1), [6,6,0,0]),
]))
story.append(qr_head_tbl)
story.append(qr_tbl)
story.append(Spacer(1, 0.5*cm))
story.append(PageBreak())
# ── DETAILED SECTIONS ─────────────────────────────────────────────────────────
for sys_data in SYSTEMS:
sc = section_color(sys_data["color_idx"])
# Section header banner
sec_hdr_data = [[
Paragraph(f"{sys_data['system'].upper()}", S_SECHEAD)
]]
sec_hdr_tbl = Table(sec_hdr_data, colWidths=[W])
sec_hdr_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), sc),
("TOPPADDING", (0,0), (-1,-1), 9),
("BOTTOMPADDING",(0,0), (-1,-1), 9),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
("ROUNDEDCORNERS",(0,0),(-1,-1), [6,6,6,6]),
]))
story.append(sec_hdr_tbl)
story.append(Spacer(1, 0.25*cm))
for tri in sys_data["triads"]:
# Triad card
# Title row
title_data = [[
Paragraph(tri["name"], style("TN", fontSize=12,
fontName="Helvetica-Bold", textColor=C_WHITE)),
Paragraph(tri["condition"], style("TC", fontSize=10,
fontName="Helvetica-Oblique", textColor=HexColor("#D0E8F0"),
alignment=TA_LEFT)),
]]
title_tbl = Table(title_data, colWidths=[W*0.4, W*0.6])
title_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_HEADER),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING",(0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
# Components table
comp_header = [
Paragraph("#", style("CH", fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER)),
Paragraph("COMPONENT", style("CH2", fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_WHITE)),
Paragraph("CLINICAL SIGNIFICANCE", style("CH3", fontSize=8.5,
fontName="Helvetica-Bold", textColor=C_WHITE)),
]
comp_rows = [comp_header]
for i, (comp, sig) in enumerate(tri["components"], 1):
bg = C_LIGHT if i % 2 == 0 else C_WHITE
comp_rows.append([
Paragraph(str(i), style(f"CN{i}", fontSize=11,
fontName="Helvetica-Bold", textColor=sc,
alignment=TA_CENTER)),
Paragraph(comp, style(f"CB{i}", fontSize=9,
fontName="Helvetica-Bold", textColor=C_NAVY)),
Paragraph(sig, style(f"CS{i}", fontSize=9,
fontName="Helvetica", textColor=C_BLACK)),
])
comp_tbl = Table(comp_rows, colWidths=[W*0.05, W*0.40, W*0.55])
comp_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), sc),
("ROWBACKGROUNDS",(0,1),(-1,-1), [C_WHITE, C_LIGHT]),
("GRID", (0,0), (-1,-1), 0.3, C_BORDER),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
# Note
note_items = []
if tri["note"]:
note_items.append(Paragraph(f"⚕ Clinical Note: {tri['note']}",
style("NT", fontSize=8.5, fontName="Helvetica",
textColor=HexColor("#1B4060"),
backColor=HexColor("#E0F0F8"),
leftIndent=6, rightIndent=6,
borderPad=4, leading=12)))
if tri["extension"]:
note_items.append(Spacer(1, 0.1*cm))
note_items.append(Paragraph(f"↗ Extension: {tri['extension']}",
style("EX", fontSize=8.5, fontName="Helvetica-Oblique",
textColor=HexColor("#8C3A1A"),
backColor=HexColor("#FDF0E8"),
leftIndent=6, rightIndent=6,
borderPad=4, leading=12)))
note_tbl_data = [[item] for item in note_items]
if note_tbl_data:
note_tbl = Table(note_tbl_data, colWidths=[W])
note_tbl.setStyle(TableStyle([
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
("BACKGROUND", (0,0),(-1,-1), HexColor("#E8F4F8")),
("BOX", (0,0),(-1,-1), 0.5, C_BORDER),
]))
else:
note_tbl = None
# Outer card wrapper
card_inner = [title_tbl, comp_tbl]
if note_tbl:
card_inner.append(note_tbl)
card_data = [[item] for item in card_inner]
card_tbl = Table(card_data, colWidths=[W])
card_tbl.setStyle(TableStyle([
("BOX", (0,0), (-1,-1), 1.0, sc),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING",(0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
]))
story.append(KeepTogether([card_tbl, Spacer(1, 0.4*cm)]))
story.append(Spacer(1, 0.3*cm))
story.append(HRFlowable(width=W, thickness=0.5, color=C_BORDER))
story.append(Spacer(1, 0.3*cm))
# ── FOOTER NOTE ───────────────────────────────────────────────────────────────
story.append(PageBreak())
footer_data = [[Paragraph(
"Sources: Sabiston Textbook of Surgery • Bailey and Love's Short Practice of Surgery • "
"Fischer's Mastery of Surgery • Current Surgical Therapy 14e • Schwartz's Principles of Surgery • "
"Maingot's Abdominal Operations • Rosen's Emergency Medicine • Harrison's Principles of Internal Medicine 22e • "
"Robbins & Kumar Pathologic Basis of Disease • Scott-Brown's Otorhinolaryngology • "
"Rockwood and Green's Fractures in Adults • Adams and Victor's Principles of Neurology • "
"Cummings Otolaryngology Head & Neck Surgery",
style("SRC", fontSize=7.5, fontName="Helvetica-Oblique",
textColor=HexColor("#555555"), alignment=TA_LEFT, leading=11))
]]
footer_tbl = Table(footer_data, colWidths=[W])
footer_tbl.setStyle(TableStyle([
("BOX", (0,0),(-1,-1), 0.5, C_BORDER),
("BACKGROUND", (0,0),(-1,-1), C_ALT),
("TOPPADDING", (0,0),(-1,-1), 8),
("BOTTOMPADDING",(0,0),(-1,-1), 8),
("LEFTPADDING", (0,0),(-1,-1), 10),
("RIGHTPADDING", (0,0),(-1,-1), 10),
]))
story.append(Paragraph("References & Sources", style("RH", fontSize=11,
fontName="Helvetica-Bold", textColor=C_NAVY)))
story.append(Spacer(1, 0.2*cm))
story.append(footer_tbl)
# ── BUILD ─────────────────────────────────────────────────────────────────────
def add_page_number(canvas, doc):
canvas.saveState()
canvas.setFont("Helvetica", 8)
canvas.setFillColor(HexColor("#888888"))
page_num = canvas.getPageNumber()
canvas.drawCentredString(A4[0]/2, 1.2*cm, f"Surgical Triads — Comparative Summary | Page {page_num}")
canvas.restoreState()
doc.build(story, onFirstPage=add_page_number, onLaterPages=add_page_number)
print(f"PDF generated: {OUTPUT}")