Create a detailed PDF of 'APPROACH TO THE PATIENT AND GENERAL EXAMINATION OF PATIENT'
mkdir -p /home/daytona/workspace/clinical-examination && ls /home/daytona/worksp…
~/clinical-examination/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor, black, white, Color
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
from reportlab.pdfgen import canvas
from reportlab.lib import colors
OUTPUT_PATH = "/home/daytona/workspace/clinical-examination/Approach_to_Patient_General_Examination.pdf"
# --- Color palette ---
DARK_BLUE = HexColor("#1A3C6B")
MED_BLUE = HexColor("#2E6DA4")
LIGHT_BLUE = HexColor("#D6E8F7")
ACCENT_TEAL = HexColor("#1A8F8F")
ACCENT_GOLD = HexColor("#C9820A")
TABLE_HEADER= HexColor("#1A3C6B")
TABLE_ALT = HexColor("#EEF4FB")
TABLE_EVEN = HexColor("#FAFCFF")
LIGHT_GRAY = HexColor("#F5F5F5")
BORDER_GRAY = HexColor("#C0C8D4")
SECTION_BAR = HexColor("#2E6DA4")
WARN_ORANGE = HexColor("#E85C0D")
WARN_BG = HexColor("#FFF3E0")
BOX_GREEN = HexColor("#1F7A4F")
BOX_GREEN_BG= HexColor("#E8F5EE")
PAGE_W, PAGE_H = A4
LEFT_M = 2.0*cm
RIGHT_M = 2.0*cm
TOP_M = 2.5*cm
BOT_M = 2.2*cm
BODY_W = PAGE_W - LEFT_M - RIGHT_M
# ===================== PAGE DECORATIONS =====================
def draw_page_decorations(canv, doc):
canv.saveState()
# Top bar
canv.setFillColor(DARK_BLUE)
canv.rect(0, PAGE_H - 1.0*cm, PAGE_W, 1.0*cm, fill=1, stroke=0)
# Title in top bar
canv.setFillColor(white)
canv.setFont("Helvetica-Bold", 9)
canv.drawString(LEFT_M, PAGE_H - 0.68*cm, "APPROACH TO THE PATIENT & GENERAL EXAMINATION")
# Right side of top bar — page number
canv.setFont("Helvetica", 8)
canv.drawRightString(PAGE_W - RIGHT_M, PAGE_H - 0.68*cm, f"Page {doc.page}")
# Bottom bar
canv.setFillColor(DARK_BLUE)
canv.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
canv.setFillColor(white)
canv.setFont("Helvetica", 7)
canv.drawCentredString(PAGE_W / 2, 0.22*cm, "Clinical Reference — S Das Manual on Clinical Surgery | Bailey & Love | Goldman-Cecil Medicine")
canv.restoreState()
def draw_cover(canv, doc):
canv.saveState()
# Deep background
canv.setFillColor(DARK_BLUE)
canv.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
# Decorative diagonal stripe
canv.setFillColor(HexColor("#1E4A80"))
from reportlab.graphics.shapes import Polygon
p = canv.beginPath()
p.moveTo(0, PAGE_H * 0.45)
p.lineTo(PAGE_W, PAGE_H * 0.35)
p.lineTo(PAGE_W, PAGE_H * 0.55)
p.lineTo(0, PAGE_H * 0.65)
p.close()
canv.drawPath(p, fill=1, stroke=0)
# White content panel
canv.setFillColor(white)
canv.roundRect(LEFT_M, PAGE_H*0.22, BODY_W, PAGE_H*0.56, 10, fill=1, stroke=0)
# Gold accent top bar on panel
canv.setFillColor(ACCENT_GOLD)
canv.roundRect(LEFT_M, PAGE_H*0.22 + PAGE_H*0.56 - 10, BODY_W, 10, 5, fill=1, stroke=0)
# Title
canv.setFillColor(DARK_BLUE)
canv.setFont("Helvetica-Bold", 22)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.68, "APPROACH TO THE PATIENT")
canv.setFont("Helvetica-Bold", 18)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.62, "AND GENERAL EXAMINATION")
# Subtitle rule
canv.setStrokeColor(ACCENT_GOLD)
canv.setLineWidth(2)
canv.line(LEFT_M + 2*cm, PAGE_H*0.595, PAGE_W - LEFT_M - 2*cm, PAGE_H*0.595)
# Sub-title text
canv.setFillColor(MED_BLUE)
canv.setFont("Helvetica-Bold", 11)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.565, "A Comprehensive Clinical Reference Guide")
# Topics
topics = [
"History Taking | Physical Examination | General Survey",
"Pallor | Cyanosis | Jaundice | Oedema | Vital Signs",
"Systematic Examination | Red Flag Signs | Clinical Diagnosis"
]
canv.setFont("Helvetica", 9)
canv.setFillColor(HexColor("#444444"))
y = PAGE_H*0.525
for t in topics:
canv.drawCentredString(PAGE_W/2, y, t)
y -= 0.45*cm
# Source badge
canv.setFillColor(LIGHT_BLUE)
canv.roundRect(LEFT_M + 3*cm, PAGE_H*0.255, BODY_W - 6*cm, 0.85*cm, 5, fill=1, stroke=0)
canv.setFillColor(DARK_BLUE)
canv.setFont("Helvetica-Bold", 8)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.273, "Sources: S Das Manual on Clinical Surgery | Bailey & Love's Surgery | Goldman-Cecil Medicine")
# Date
canv.setFillColor(white)
canv.setFont("Helvetica", 9)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.17, "Prepared: July 2026")
canv.restoreState()
# ===================== STYLES =====================
styles = getSampleStyleSheet()
h1 = ParagraphStyle("H1", fontName="Helvetica-Bold", fontSize=16, textColor=white,
spaceAfter=2, spaceBefore=6, leading=20, leftIndent=0)
h2 = ParagraphStyle("H2", fontName="Helvetica-Bold", fontSize=13, textColor=DARK_BLUE,
spaceAfter=4, spaceBefore=14, leading=17, borderPad=3)
h3 = ParagraphStyle("H3", fontName="Helvetica-Bold", fontSize=11, textColor=ACCENT_TEAL,
spaceAfter=3, spaceBefore=10, leading=14)
h4 = ParagraphStyle("H4", fontName="Helvetica-Bold", fontSize=10, textColor=MED_BLUE,
spaceAfter=2, spaceBefore=6, leading=13)
body = ParagraphStyle("Body", fontName="Helvetica", fontSize=9.5, textColor=black,
spaceAfter=5, spaceBefore=2, leading=14, alignment=TA_JUSTIFY)
bullet_style = ParagraphStyle("Bullet", fontName="Helvetica", fontSize=9.5, textColor=black,
spaceAfter=3, spaceBefore=1, leading=13,
leftIndent=12, bulletIndent=4, alignment=TA_LEFT)
sub_bullet = ParagraphStyle("SubBullet", fontName="Helvetica", fontSize=9, textColor=HexColor("#333333"),
spaceAfter=2, spaceBefore=1, leading=12,
leftIndent=26, bulletIndent=16)
caption = ParagraphStyle("Caption", fontName="Helvetica-Oblique", fontSize=8, textColor=HexColor("#555555"),
spaceAfter=4, spaceBefore=1, leading=11, alignment=TA_CENTER)
note_style = ParagraphStyle("Note", fontName="Helvetica-Oblique", fontSize=9, textColor=HexColor("#444444"),
spaceAfter=5, spaceBefore=3, leading=13, leftIndent=8)
box_body = ParagraphStyle("BoxBody", fontName="Helvetica", fontSize=9.5, textColor=black,
spaceAfter=4, spaceBefore=2, leading=13, leftIndent=6)
warn_body = ParagraphStyle("WarnBody", fontName="Helvetica", fontSize=9.5, textColor=HexColor("#7B2800"),
spaceAfter=4, spaceBefore=2, leading=13, leftIndent=6)
toc_style = ParagraphStyle("TOC", fontName="Helvetica", fontSize=10, textColor=DARK_BLUE,
spaceAfter=4, spaceBefore=2, leading=15, leftIndent=10)
toc_h = ParagraphStyle("TOCH", fontName="Helvetica-Bold", fontSize=10, textColor=DARK_BLUE,
spaceAfter=3, spaceBefore=6, leading=14, leftIndent=0)
# ===================== HELPER FLOWABLES =====================
def section_header(title):
"""Colored section bar"""
data = [[Paragraph(title, h1)]]
t = Table(data, colWidths=[BODY_W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), SECTION_BAR),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING",(0,0), (-1,-1), 7),
("ROUNDEDCORNERS", [4]),
]))
return t
def info_box(title, items, color=BOX_GREEN, bg=BOX_GREEN_BG, bullet_char="✔"):
"""Green (or colored) info box with bullet list"""
flowables = []
title_para = Paragraph(f"<b>{title}</b>", ParagraphStyle("BoxTitle", fontName="Helvetica-Bold",
fontSize=10, textColor=color, spaceAfter=4, leading=14, leftIndent=6))
rows = [[title_para]]
for item in items:
rows.append([Paragraph(f"{bullet_char} {item}", box_body)])
t = Table(rows, colWidths=[BODY_W - 1.2*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("BOX", (0,0), (-1,-1), 1, color),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("ROUNDEDCORNERS", [5]),
]))
return t
def warning_box(title, items):
"""Orange warning/red flag box"""
flowables = []
title_para = Paragraph(f"⚠ {title}", ParagraphStyle("WarnTitle", fontName="Helvetica-Bold",
fontSize=10, textColor=WARN_ORANGE, spaceAfter=4, leading=14, leftIndent=6))
rows = [[title_para]]
for item in items:
rows.append([Paragraph(f"• {item}", warn_body)])
t = Table(rows, colWidths=[BODY_W - 1.2*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), WARN_BG),
("BOX", (0,0), (-1,-1), 1.5, WARN_ORANGE),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("ROUNDEDCORNERS", [5]),
]))
return t
def make_table(headers, rows, col_widths=None):
if col_widths is None:
col_widths = [BODY_W / len(headers)] * len(headers)
data = [[Paragraph(f"<b>{h}</b>", ParagraphStyle("TH", fontName="Helvetica-Bold",
fontSize=9, textColor=white, leading=12)) for h in headers]]
for i, row in enumerate(rows):
data.append([Paragraph(str(c), ParagraphStyle("TD", fontName="Helvetica",
fontSize=9, textColor=black, leading=12,
leftIndent=2)) for c in row])
t = Table(data, colWidths=col_widths, repeatRows=1)
styles_list = [
("BACKGROUND", (0,0), (-1,0), TABLE_HEADER),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWBACKGROUNDS", (0,1), (-1,-1), [TABLE_EVEN, TABLE_ALT]),
("BOX", (0,0), (-1,-1), 0.8, BORDER_GRAY),
("INNERGRID", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
]
t.setStyle(TableStyle(styles_list))
return t
def B(text):
return Paragraph(f"• {text}", bullet_style)
def SB(text):
return Paragraph(f"– {text}", sub_bullet)
def P(text):
return Paragraph(text, body)
def H2(text):
return Paragraph(text, h2)
def H3(text):
return Paragraph(text, h3)
def H4(text):
return Paragraph(text, h4)
def SP(h=0.2):
return Spacer(1, h*cm)
def HR():
return HRFlowable(width="100%", thickness=0.5, color=BORDER_GRAY, spaceAfter=4, spaceBefore=4)
# ===================== BUILD PDF =====================
doc = BaseDocTemplate(
OUTPUT_PATH,
pagesize=A4,
leftMargin=LEFT_M,
rightMargin=RIGHT_M,
topMargin=TOP_M + 0.5*cm,
bottomMargin=BOT_M + 0.5*cm,
title="Approach to the Patient and General Examination",
author="Clinical Reference",
subject="Clinical Medicine",
)
cover_frame = Frame(0, 0, PAGE_W, PAGE_H, id="cover")
content_frame = Frame(LEFT_M, BOT_M + 0.7*cm, BODY_W, PAGE_H - TOP_M - BOT_M - 1.5*cm, id="content")
cover_template = PageTemplate(id="cover", frames=[cover_frame], onPage=draw_cover)
content_template = PageTemplate(id="content", frames=[content_frame], onPage=draw_page_decorations)
doc.addPageTemplates([cover_template, content_template])
story = []
# ---- COVER PAGE ----
story.append(Spacer(1, PAGE_H)) # Filled by draw_cover
# Switch to content template
from reportlab.platypus import NextPageTemplate
story.append(NextPageTemplate("content"))
story.append(PageBreak())
# ========== TABLE OF CONTENTS ==========
story.append(section_header("TABLE OF CONTENTS"))
story.append(SP(0.4))
toc_entries = [
("1", "Approach to the Patient — Overview"),
("2", "History Taking"),
(" 2.1", "Patient Particulars"),
(" 2.2", "Chief Complaints"),
(" 2.3", "History of Present Illness"),
(" 2.4", "Past History, Family History & Personal History"),
("3", "Physical Examination — Framework"),
("4", "General Survey (Systemic Inspection)"),
(" 4.1", "General Assessment of Illness"),
(" 4.2", "Mental State and Level of Consciousness"),
(" 4.3", "Nutritional Status and Body Build"),
(" 4.4", "Posture, Attitude, and Decubitus"),
(" 4.5", "Facies"),
("5", "Colour of the Skin — Pallor, Cyanosis, Jaundice"),
(" 5.1", "Pallor"),
(" 5.2", "Cyanosis (Peripheral & Central)"),
(" 5.3", "Jaundice"),
("6", "Vital Signs"),
(" 6.1", "Pulse"),
(" 6.2", "Blood Pressure"),
(" 6.3", "Temperature"),
(" 6.4", "Respiratory Rate"),
("7", "Peripheral Signs — Hands, Nails, Eyes, Lymph Nodes"),
("8", "Systemic Examination Checklist"),
(" 8.1", "Head and Neck"),
(" 8.2", "Upper Limbs"),
(" 8.3", "Thorax (Chest)"),
(" 8.4", "Abdomen"),
(" 8.5", "Lower Limbs"),
("9", "Red Flag Signs"),
("10", "Provisional Diagnosis and Special Investigations"),
("11", "Clinical Diagnosis and Treatment Plan"),
("12", "Summary Table — Key Clinical Signs at a Glance"),
]
for num, title in toc_entries:
indent = 10 if num.startswith(" ") else 0
style = ParagraphStyle("TOCEntry", fontName="Helvetica", fontSize=9.5,
textColor=DARK_BLUE, leftIndent=indent + 10,
spaceAfter=2, leading=14,
fontName="Helvetica-Bold" if not num.startswith(" ") else "Helvetica")
story.append(Paragraph(f"{num.strip()}. {title}", style))
story.append(PageBreak())
# ========== SECTION 1: OVERVIEW ==========
story.append(section_header("1. APPROACH TO THE PATIENT — OVERVIEW"))
story.append(SP(0.3))
story.append(P(
"The approach to the patient is the foundation of clinical medicine. A good clinician begins "
"observing the patient from the moment they enter the consulting room — noting their gait, posture, "
"facial expression, breathing pattern, and apparent level of distress. The student should learn a "
"systematic scheme and make it a reflex, applicable to every patient in every clinical setting."
))
story.append(SP(0.2))
story.append(info_box("The General Clinical Scheme", [
"1. History Taking",
"2. Physical Examination (General Survey + Local Examination + General Examination)",
"3. Special Investigations",
"4. Provisional and Clinical Diagnosis",
"5. Treatment — Medical and/or Surgical",
"6. Progress Monitoring (Post-operative or Post-treatment period)",
"7. Follow-Up",
"8. Termination / Discharge / Outcome",
], color=BOX_GREEN, bg=BOX_GREEN_BG))
story.append(SP(0.3))
story.append(P(
"It is helpful to have the accompanying relative or carer present during the early part of history-taking. "
"They can provide valuable information about the type of injury, changes in behaviour, or social circumstances "
"that the patient themselves may not be able to relay accurately. Always begin the consultation by addressing "
"the patient by name — this builds rapport and psychological confidence."
))
story.append(PageBreak())
# ========== SECTION 2: HISTORY TAKING ==========
story.append(section_header("2. HISTORY TAKING"))
story.append(SP(0.3))
story.append(H2("2.1 Patient Particulars"))
story.append(P("Before interrogating about complaints, the following patient particulars must be documented:"))
headers = ["Parameter", "Clinical Significance"]
rows = [
["Name", "Builds rapport; aids identification; psychological benefit pre- and post-operatively."],
["Age", "Congenital anomalies present since birth; sarcomas in teenagers; carcinomas >40 yrs; BPH in old age."],
["Sex", "Thyroid disease, visceroptosis, cystitis more common in females; gastric and lung carcinoma commoner in males. Haemophilia — males only."],
["Religion / Occupation", "Occupation may expose patient to toxins (chemical, physical, biological). Religion may affect dietary history or procedures."],
["Address", "Identifies geographic disease prevalence; enables follow-up and contact."],
["Date of Admission", "Provides timeline; important for medico-legal documentation."],
]
story.append(make_table(headers, rows, col_widths=[4.5*cm, BODY_W - 4.5*cm]))
story.append(SP(0.3))
story.append(H2("2.2 Chief Complaints"))
story.append(P(
"The chief complaint is the main reason the patient presents. Record in the patient's own words, "
"without medical interpretation. List complaints in chronological order (earliest first). "
"Each complaint should include its <b>duration</b>. For example: "
"<i>'Swelling in the neck for 3 months, pain for 2 weeks, difficulty swallowing for 1 week.'</i>"
))
story.append(SP(0.2))
story.append(H2("2.3 History of Present Illness (HPI)"))
story.append(P("Every complaint must be explored systematically using the <b>SOCRATES</b> framework (or equivalent):"))
headers2 = ["Dimension", "Key Questions"]
rows2 = [
["Site", "Where exactly is the problem? Point with one finger if possible."],
["Onset", "When did it start? Was onset sudden or gradual?"],
["Character", "Describe the quality — e.g., sharp, dull, burning, throbbing, colicky."],
["Radiation / Spread", "Does the pain/swelling spread to another area?"],
["Associations", "Any fever, weight loss, night sweats, vomiting, dysphagia, dyspnoea?"],
["Time course", "Constant or intermittent? Getting better, worse or same? Diurnal variation?"],
["Exacerbating / Relieving", "What makes it worse or better? Effect of food, position, activity, drugs?"],
["Severity", "Pain score 0–10; functional limitation; effect on daily life."],
]
story.append(make_table(headers2, rows2, col_widths=[4.0*cm, BODY_W - 4.0*cm]))
story.append(SP(0.3))
story.append(H2("2.4 Past, Family and Personal History"))
story.append(H4("Past History"))
story.append(P(
"Ask about previous illnesses, surgeries, hospitalisations, blood transfusions, allergies (especially to drugs "
"and iodine-containing contrast), and previous medications. Previous operations must be recorded with dates — "
"students who practice this habit will save many potential catastrophes."
))
story.append(H4("Family History"))
story.append(P(
"Many diseases recur in families: haemophilia, tuberculosis, diabetes mellitus, essential hypertension, "
"peptic ulcer, majority of cancers (especially breast cancer), fissure-in-ano, and piles. Enquire about "
"parents (health or cause of death), siblings, and children."
))
story.append(H4("Personal History"))
story.append(P(
"Record: smoking (type, amount), alcohol (quantity and frequency), diet (vegetarian/non-vegetarian, regularity), "
"marital status. In <b>women</b>: menstrual history (regularity, duration, dysmenorrhoea, LMP), number of "
"pregnancies, deliveries, miscarriages, Caesarean sections, vaginal discharge."
))
story.append(PageBreak())
# ========== SECTION 3: PHYSICAL EXAMINATION FRAMEWORK ==========
story.append(section_header("3. PHYSICAL EXAMINATION — FRAMEWORK"))
story.append(SP(0.3))
story.append(P(
"Physical examination is divided into three main components. Each component is performed "
"using a systematic sequence: <b>Inspection → Palpation → Percussion → Auscultation</b>."
))
story.append(SP(0.2))
headers3 = ["Component", "Description", "Includes"]
rows3 = [
["A. General Survey",
"Assessment of the patient as a whole",
"Mental state, build, nutrition, posture, skin colour, pulse, temperature, respiration"],
["B. Local Examination",
"Focused examination of the presenting complaint/affected region",
"Inspection, palpation, percussion, auscultation of the local area"],
["C. General Examination",
"Systematic examination of ALL body systems regardless of complaint",
"Head & neck, limbs, thorax, abdomen, genitalia, nervous system"],
]
story.append(make_table(headers3, rows3, col_widths=[3.8*cm, 5.0*cm, BODY_W - 8.8*cm]))
story.append(SP(0.3))
story.append(info_box("Environment Requirements for Examination", [
"Perform in good natural daylight (artificial light may miss faint jaundice).",
"Patient should be asked to remove all clothing; covered by a dressing gown.",
"For female patients, an attendant nurse must be present.",
"Examination begins when the patient enters the clinic — observe gait, posture, distress.",
"The patient must be placed in the appropriate position for each part of the examination.",
], color=MED_BLUE, bg=LIGHT_BLUE))
story.append(PageBreak())
# ========== SECTION 4: GENERAL SURVEY ==========
story.append(section_header("4. GENERAL SURVEY (SYSTEMIC INSPECTION)"))
story.append(SP(0.3))
story.append(H2("4.1 General Assessment of Illness"))
story.append(P(
"This is the <b>first and most important</b> step. Assess whether the patient looks well, ill, or critically ill. "
"In <b>severely ill patients</b>, time must not be wasted — the clinician should rapidly move to the local examination "
"and institute emergency treatment. The severity of illness guides the pace and depth of the rest of the examination."
))
story.append(SP(0.2))
headers_ill = ["Grade", "Appearance", "Clinical Implication"]
rows_ill = [
["Well", "Alert, comfortable, no distress", "Condition is non-urgent; full examination can proceed."],
["Ill (mild-moderate)", "Looks unwell, some distress, cooperative", "Focused examination with priority for key findings."],
["Very ill / Critically ill", "Severe distress, pale, tachycardic, hypotensive", "Rapid assessment; institute resuscitation immediately."],
["Moribund", "Barely responsive, signs of end-stage disease", "Immediate palliative or resuscitative measures; senior review."],
]
story.append(make_table(headers_ill, rows_ill, col_widths=[3.2*cm, 5.5*cm, BODY_W - 8.7*cm]))
story.append(SP(0.3))
story.append(H2("4.2 Mental State and Level of Consciousness"))
story.append(P(
"Mental state assessment is particularly important in head injury, metabolic encephalopathy, "
"drug overdose, and critically ill patients. An intelligent patient provides a reliable history; "
"a patient with impaired consciousness may require collateral history from relatives."
))
story.append(SP(0.1))
story.append(P("<b>Five Stages of Level of Consciousness:</b>"))
items_loc = [
"Stage 1 — Fully conscious: Perfect orientation to time, place, and person.",
"Stage 2 — Confused: Oriented but slow to respond; may be disoriented to time.",
"Stage 3 — Stuporous / Semi-conscious: Responds to verbal commands; abnormal movements.",
"Stage 4 — Unconscious but responding to painful stimuli.",
"Stage 5 — Deeply unconscious: No response to any stimuli.",
]
for item in items_loc:
story.append(B(item))
story.append(SP(0.1))
story.append(P(
"The <b>Glasgow Coma Scale (GCS)</b> provides a standardised scoring of consciousness: "
"Eye opening (1–4), Verbal response (1–5), Motor response (1–6). Maximum = 15 (fully conscious); "
"Minimum = 3 (deeply unconscious). GCS ≤8 indicates severe injury; <b>secure airway.</b>"
))
story.append(SP(0.2))
story.append(H2("4.3 Nutritional Status and Body Build"))
story.append(P(
"Observe the patient's body build and state of nutrition from the moment they enter:"
))
headers_nutr = ["Assessment", "Features", "Clinical Significance"]
rows_nutr = [
["Well-nourished", "Normal BMI (18.5–24.9), good muscle bulk", "Baseline normal."],
["Overweight / Obese", "BMI >25 / >30, excess adipose tissue", "Risk of DM, CVD, OA, OSA, surgical complications."],
["Underweight / Malnourished", "BMI <18.5, visible bony prominences, hollow cheeks", "Malabsorption, malignancy, chronic infection, anorexia."],
["Cachexia", "Severe wasting, temporal hollowing, sunken eyes", "Malignancy, AIDS, severe chronic disease, TB."],
["Obesity with striae / buffalo hump", "Central obesity, moon face, purple striae", "Cushing's syndrome."],
]
story.append(make_table(headers_nutr, rows_nutr, col_widths=[4.0*cm, 5.5*cm, BODY_W - 9.5*cm]))
story.append(SP(0.3))
story.append(H2("4.4 Posture, Attitude, and Decubitus"))
story.append(P(
"<b>Decubitus</b> refers to the position the patient adopts in bed. It can be highly informative:"
))
dec_data = [
["Curled on side away from light", "Cerebral irritation, meningitis"],
["Lying still (avoids movement)", "Peritonitis (generalised)"],
["Cannot lie flat, prefers sitting up (orthopnoea)", "Left heart failure, severe asthma, COPD"],
["Leaning forward (tripod position)", "Acute pericarditis, severe respiratory distress"],
["Opisthotonos (rigid arching of back)", "Tetanus, severe meningitis"],
["Decorticate posturing (arms flexed)", "Bilateral cortical damage above midbrain"],
["Decerebrate posturing (arms extended)", "Brainstem injury — severe prognosis"],
]
story.append(make_table(["Position / Posture", "Associated Condition"], dec_data,
col_widths=[6.0*cm, BODY_W - 6.0*cm]))
story.append(SP(0.3))
story.append(H2("4.5 Facies"))
story.append(P(
"The face is the 'mirror of the mind'. A good clinician can assess the depth of disease just by "
"looking at the patient's face. Recognising characteristic facies is a key clinical skill:"
))
facies_data = [
["Facies hippocratica", "Sunken eyes, pale, hollow cheeks, cold extremities", "Generalised peritonitis, terminal illness"],
["Risus sardonicus", "Fixed grin due to facial muscle spasm", "Tetanus"],
["Mask face (expressionless)", "Reduced blinking, fixed stare", "Parkinsonism"],
["Moon face", "Round, plethoric face", "Cushing's syndrome, steroid therapy"],
["Adenoid facies", "Open mouth, upturned nose, elongated face", "Hypertrophied adenoids, chronic mouth breathing"],
["Acromegalic facies", "Prominent supraorbital ridge, large jaw, large nose", "Acromegaly (GH excess)"],
["Myxoedema facies", "Puffy, dry skin, periorbital oedema, loss of outer eyebrow", "Hypothyroidism"],
["Thyrotoxic facies", "Exophthalmos, lid lag, anxious expression", "Hyperthyroidism (Graves' disease)"],
["Cushingoid facies", "Moon face, facial plethora, hirsutism", "Cushing's syndrome"],
["Leonine facies", "Coarse thickened skin, loss of eyebrows", "Lepromatous leprosy"],
]
story.append(make_table(["Facies Type", "Features", "Associated Condition"], facies_data,
col_widths=[4.0*cm, 5.5*cm, BODY_W - 9.5*cm]))
story.append(PageBreak())
# ========== SECTION 5: COLOUR / PALLOR / CYANOSIS / JAUNDICE ==========
story.append(section_header("5. COLOUR OF THE SKIN — PALLOR, CYANOSIS, JAUNDICE"))
story.append(SP(0.3))
story.append(H2("5.1 Pallor"))
story.append(P(
"Pallor (unusual paleness of skin and mucous membranes) results from reduced blood flow to "
"superficial vessels or reduced haemoglobin. It is seen in <b>anaemia, haemorrhage, shock, and intense emotion</b>."
))
story.append(P("<b>Where to look for pallor:</b>"))
for s in ["Lower palpebral conjunctiva (most reliable site)",
"Mucous membrane of lips and buccal mucosa",
"Nail beds",
"Palmar creases (pallor of creases = Hb <7 g/dL)"]:
story.append(B(s))
story.append(SP(0.2))
story.append(H2("5.2 Cyanosis"))
story.append(P(
"Cyanosis is the bluish or purplish tinge of skin or mucous membranes due to excess <b>reduced (deoxygenated) "
"haemoglobin</b> in superficial vessels. Cyanosis requires a minimum of <b>5 g/dL of reduced haemoglobin</b> "
"to be visible. Therefore, cyanosis cannot be detected in severe anaemia."
))
story.append(SP(0.2))
cyan_data = [
["Type", "Peripheral Cyanosis", "Central Cyanosis"],
["Mechanism", "Excessive O₂ extraction in peripheral capillaries (slowed flow)", "Inadequate oxygenation of arterial blood in the lungs"],
["Causes", "Cold exposure, reduced cardiac output, peripheral vascular disease, shock", "Lung disease (severe pneumonia, ARDS, PE), R→L cardiac shunts (congenital heart disease)"],
["Sites to examine", "Nail beds, fingertips, tip of nose, skin of palms and toes", "Tongue, lips, buccal mucosa (tongue affected — diagnostic)"],
["Tongue involvement", "NOT affected", "YES — tongue is cyanosed (distinguishing feature)"],
["SaO₂", "Normal or near-normal arterial O₂", "Reduced arterial O₂ saturation (<94%)"],
]
t_cyan = Table(cyan_data, colWidths=[3.5*cm, (BODY_W-3.5*cm)/2, (BODY_W-3.5*cm)/2])
t_cyan.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TABLE_HEADER),
("TEXTCOLOR", (0,0), (-1,0), white),
("BACKGROUND", (0,0), (0,-1), LIGHT_BLUE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTNAME", (0,0), (0,-1), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS",(0,1),(-1,-1),[TABLE_EVEN, TABLE_ALT]),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
]))
story.append(t_cyan)
story.append(SP(0.15))
story.append(P(
"<b>Special note:</b> Carbon monoxide poisoning produces <b>cherry-red discolouration</b> (not cyanosis). "
"Methaemoglobinaemia (due to drugs like phenacetin) produces cyanosis with <b>normal arterial O₂ tension</b>."
))
story.append(SP(0.3))
story.append(H2("5.3 Jaundice"))
story.append(P(
"Jaundice (icterus) is the yellow discolouration of skin and mucous membranes due to excess "
"<b>bilirubin</b> in the plasma. Bilirubin is a lipid-soluble yellow pigment derived from haem breakdown. "
"Jaundice becomes clinically detectable when serum bilirubin exceeds <b>2–3 mg/dL</b> (normal <1 mg/dL)."
))
story.append(SP(0.1))
story.append(P("<b>Where to look for jaundice:</b>"))
for s in ["Sclera of the eyeball (best site — earliest sign)",
"Skin (especially fair-skinned individuals; may be missed in dark skin)",
"Mucous membranes of hard palate and under the tongue",
"Urine (dark urine — conjugated bilirubinuria)",
"Stool (clay-coloured = obstructive jaundice)"]:
story.append(B(s))
story.append(SP(0.2))
jaund_data = [
["Type", "Serum Bilirubin", "Colour", "Urine", "Stools", "Common Causes"],
["Pre-hepatic\n(Haemolytic)", "Unconjugated ↑", "Lemon-yellow", "Normal / dark (urobilinogen)", "Normal / dark", "Haemolytic anaemia, sickle cell, malaria"],
["Hepatic\n(Hepatocellular)", "Both ↑", "Yellow-orange", "Dark (bilirubin + urobilinogen)", "Pale", "Viral hepatitis, alcoholic liver disease, cirrhosis"],
["Post-hepatic\n(Obstructive)", "Conjugated ↑↑", "Deep greenish-yellow (olive)", "Dark (bilirubinuria)", "Pale / clay-coloured", "Gallstones, carcinoma head of pancreas, cholangiocarcinoma"],
]
story.append(make_table(["Type", "Serum Bilirubin", "Colour", "Urine", "Stools", "Common Causes"],
jaund_data[1:],
col_widths=[3.0*cm, 2.5*cm, 2.2*cm, 2.5*cm, 2.0*cm, BODY_W - 12.2*cm]))
story.append(PageBreak())
# ========== SECTION 6: VITAL SIGNS ==========
story.append(section_header("6. VITAL SIGNS"))
story.append(SP(0.3))
story.append(P(
"Vital signs form the cornerstone of general examination. They should be recorded at the beginning "
"of every clinical encounter and are essential for emergency triage (ABCDE approach)."
))
story.append(SP(0.2))
story.append(H2("6.1 Pulse"))
story.append(P(
"Palpate the radial pulse (at the wrist, lateral to flexor carpi radialis tendon) using the index and "
"middle fingers. Note the following parameters:"
))
pulse_data = [
["Rate", "Normal: 60–100 bpm. Bradycardia <60 bpm; Tachycardia >100 bpm.", "Heart rate, rhythm disorders, medications"],
["Rhythm", "Regular, regularly irregular (e.g. 2nd degree AV block), or irregularly irregular (AF).", "Arrhythmias — atrial fibrillation is irregularly irregular"],
["Volume / Amplitude", "Normal, bounding (high), weak/thready (low).", "Bounding: fever, AR, thyrotoxicosis. Thready: shock, hypovolaemia."],
["Character", "Slow rising plateau (AS), collapsing/water-hammer (AR), bisferiens, pulsus paradoxus.", "Aortic valve disease, pericardial effusion"],
["Radio-radial delay", "Delay between right and left radial pulses.", "Coarctation of aorta, subclavian artery stenosis"],
["Radio-femoral delay", "Delay between radial and femoral pulses.", "Coarctation of aorta (classic sign)"],
]
story.append(make_table(["Parameter", "Description / Normal Values", "Clinical Significance"],
pulse_data, col_widths=[3.5*cm, 6.5*cm, BODY_W - 10.0*cm]))
story.append(SP(0.3))
story.append(H2("6.2 Blood Pressure"))
story.append(P(
"Measure in both arms initially. Record systolic (Korotkoff I) and diastolic (Korotkoff V) pressures. "
"Normal: <120/80 mmHg. Hypertension: ≥130/80 mmHg (ACC/AHA 2017) or ≥140/90 mmHg (traditional)."
))
bp_data = [
["Hypertension Grade 1", "130–139 / 80–89 mmHg", "Lifestyle modification; consider medication if high CV risk."],
["Hypertension Grade 2", "≥140 / ≥90 mmHg", "Antihypertensive therapy indicated."],
["Hypertensive Urgency", "≥180/120 mmHg, no organ damage", "Oral antihypertensives; close monitoring."],
["Hypertensive Emergency", "≥180/120 mmHg + organ damage", "IV antihypertensives; ICU admission."],
["Hypotension", "<90/60 mmHg", "Dehydration, shock, adrenal insufficiency, sepsis."],
["Postural hypotension", "Drop ≥20 mmHg systolic on standing", "Dehydration, autonomic neuropathy, elderly, medications."],
["Pulse pressure widened (>60 mmHg)", "Aortic regurgitation, arteriovenous fistula, thyrotoxicosis"],
["Pulse pressure narrowed (<25 mmHg)", "Aortic stenosis, pericardial effusion, hypovolaemic shock"],
]
story.append(make_table(["BP Category", "Values", "Clinical Action"],
[r for r in bp_data if len(r)==3],
col_widths=[4.5*cm, 4.5*cm, BODY_W - 9.0*cm]))
story.append(SP(0.3))
story.append(H2("6.3 Temperature"))
body_temp = [
["Normal", "36.5–37.2°C (oral); 36.8°C mean axillary; rectal 0.3–0.5°C higher"],
["Low-grade fever", "37.3–38.0°C — early infection, viral illness, malignancy"],
["Moderate fever", "38.0–39.0°C — bacterial infection, systemic inflammatory disease"],
["High fever", "39.0–40.0°C — severe bacterial infection, malaria"],
["Hyperpyrexia", ">40.0°C — heat stroke, CNS infection, malignant hyperthermia"],
["Hypothermia", "<35.0°C — exposure, myxoedema, hypoadrenalism, septic shock"],
]
story.append(make_table(["Category", "Temperature Range and Causes"], body_temp,
col_widths=[4.0*cm, BODY_W - 4.0*cm]))
story.append(SP(0.3))
story.append(H2("6.4 Respiratory Rate"))
story.append(P(
"The respiratory rate is often the earliest vital sign to change in clinical deterioration and is "
"frequently underrecorded. It must be counted for a <b>full 60 seconds</b> while the patient is unaware. "
"Normal adult rate: <b>12–20 breaths/min</b>."
))
for item in [
"Tachypnoea (>20/min): pneumonia, pulmonary embolism, cardiac failure, metabolic acidosis, anxiety, sepsis.",
"Bradypnoea (<12/min): opioid overdose, CNS depression, raised intracranial pressure.",
"Cheyne-Stokes respiration: cyclical waxing and waning; seen in cardiac failure, uraemia, brain injury.",
"Kussmaul breathing: deep, rapid, sighing — diabetic ketoacidosis (metabolic acidosis).",
"Biot's breathing: irregular with periods of apnoea — brainstem injury."
]:
story.append(B(item))
story.append(PageBreak())
# ========== SECTION 7: PERIPHERAL SIGNS ==========
story.append(section_header("7. PERIPHERAL SIGNS — HANDS, NAILS, EYES, LYMPH NODES"))
story.append(SP(0.3))
story.append(H2("7.1 Hands and Nails"))
nail_data = [
["Clubbing", "Obliteration of the nail-fold angle; 'drumstick' fingers", "Lung carcinoma, bronchiectasis, empyema, cyanotic CHD, cirrhosis, IBD, infective endocarditis"],
["Koilonychia (spoon nails)", "Concave / spoon-shaped nails", "Iron deficiency anaemia (chronic)"],
["Leukonychia", "White nails (Terry's nails — white with pink tip)", "Hypoalbuminaemia, cirrhosis, nephrotic syndrome"],
["Onycholysis", "Separation of nail plate from bed", "Thyrotoxicosis, psoriasis, trauma"],
["Beau's lines", "Transverse grooves in the nails", "Previous serious illness, chemotherapy"],
["Splinter haemorrhages", "Linear dark streaks under nail", "Infective endocarditis (classic), vasculitis, trauma"],
["Peripheral cyanosis", "Bluish discolouration of nail bed", "Reduced peripheral perfusion, shock, cold"],
["Pallor of nail bed", "Pink → pale", "Anaemia"],
]
story.append(make_table(["Sign", "Appearance", "Clinical Association"], nail_data,
col_widths=[3.5*cm, 4.5*cm, BODY_W - 8.0*cm]))
story.append(SP(0.3))
story.append(H2("7.2 Eyes"))
eye_data = [
["Jaundice (icterus)", "Yellow sclera", "Hyperbilirubinaemia (earliest site)"],
["Pallor of conjunctiva", "Pale lower palpebral conjunctiva", "Anaemia"],
["Exophthalmos / Proptosis", "Protrusion of eyeball", "Thyrotoxicosis (Graves'), orbital mass"],
["Lid lag", "Upper lid lags behind eyeball on downward gaze", "Thyrotoxicosis"],
["Periorbital oedema", "Puffiness around eyes", "Myxoedema, nephrotic syndrome, angioedema"],
["Kayser-Fleischer rings", "Golden-brown ring at cornea periphery", "Wilson's disease"],
["Corneal arcus", "Grey-white arc around cornea", "Hypercholesterolaemia (if <45 yrs); normal aging"],
["Xanthelasma", "Yellow plaques on eyelids", "Hyperlipidaemia"],
["Argyll Robertson pupil", "Accommodates but does not react to light", "Neurosyphilis"],
["Horner's syndrome", "Ptosis, miosis, anhidrosis", "Pancoast tumour, carotid dissection, brainstem lesion"],
]
story.append(make_table(["Sign", "Appearance", "Clinical Significance"], eye_data,
col_widths=[3.8*cm, 4.5*cm, BODY_W - 8.3*cm]))
story.append(SP(0.3))
story.append(H2("7.3 Lymphadenopathy"))
story.append(P(
"Lymph node enlargement must always be assessed in context of the chief complaint. "
"Examine all regional lymph node groups systematically. Note size, consistency, tenderness, "
"fixity, and overlying skin changes."
))
lymph_data = [
["Cervical (anterior)", "Dental infection, oral malignancy, lymphoma"],
["Cervical (posterior)", "EBV (infectious mononucleosis), rubella, TB, lymphoma"],
["Left supraclavicular (Virchow's node)", "Intra-abdominal malignancy (stomach, colon, pancreas) — Troisier's sign"],
["Axillary", "Breast carcinoma, upper limb infection, lymphoma"],
["Inguinal", "Lower limb infection, STIs, pelvic/abdominal malignancy"],
["Generalised lymphadenopathy", "EBV, HIV, lymphoma, leukaemia, sarcoidosis, SLE"],
]
story.append(make_table(["Lymph Node Group", "Clinical Significance"], lymph_data,
col_widths=[5.5*cm, BODY_W - 5.5*cm]))
story.append(SP(0.2))
story.append(P(
"<b>Important:</b> In case of any malignant swelling, always examine regional and distant lymph nodes. "
"In case of suspected lymphoma, examine <i>all</i> nodal groups including mediastinal (chest X-ray) and "
"abdominal (USS/CT) lymph nodes."
))
story.append(PageBreak())
# ========== SECTION 8: SYSTEMIC EXAMINATION CHECKLIST ==========
story.append(section_header("8. SYSTEMIC EXAMINATION CHECKLIST"))
story.append(SP(0.3))
story.append(H2("8.1 Head and Neck"))
for item in [
"Skull: shape, tenderness, swellings (erosion palpable in dermoid cysts — Feeling's test).",
"Eyes: jaundice, pallor, exophthalmos, lid lag, Horner's, corneal abnormalities.",
"Ears: discharge, deformity, tenderness (mastoid).",
"Nose: discharge, deformity, epistaxis.",
"Mouth and Throat: dentition, gum condition, tonsils, tongue (size, colour, coating, fissures).",
"Neck: trachea position (deviation indicates mediastinal shift), JVP, thyroid, lymph nodes, carotid bruits.",
"JVP (Jugular Venous Pressure): normal <4 cm above sternal angle; elevated in right heart failure, SVC obstruction, cardiac tamponade.",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("8.2 Upper Limbs"))
for item in [
"General examination: wasting, deformities, scars.",
"Vascular: radial pulse (rate, rhythm, volume, character), Allen's test if needed.",
"Neurological: power (MRC grading 0–5), tone, reflexes (biceps C5–6, triceps C7, supinator C5–6), sensation.",
"Axillae: lymph nodes (multiple groups — anterior, posterior, lateral, medial, apical).",
"Joints: ROM, deformity, swelling, crepitus.",
"Nails: clubbing, koilonychia, leukonychia, splinter haemorrhages.",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("8.3 Thorax — Respiratory and Cardiovascular"))
story.append(P("<b>Respiratory Examination:</b>"))
for item in [
"Type of chest: barrel (emphysema), pigeon (rickets/childhood asthma), funnel (pectus excavatum), kyphoscoliosis.",
"Tracheal position: central or deviated.",
"Expansion: symmetrical vs. reduced (on affected side in collapse, effusion, pneumothorax).",
"Percussion: resonant (normal), dull (consolidation/effusion), stony dull (effusion), hyper-resonant (pneumothorax/emphysema).",
"Auscultation: breath sounds, added sounds (crackles, wheeze, pleural rub).",
]:
story.append(B(item))
story.append(SP(0.15))
story.append(P("<b>Cardiovascular Examination:</b>"))
for item in [
"Inspection: chest wall pulsations, dilated veins, scars (median sternotomy, CABG, pacemaker).",
"Apex beat: normally 5th ICS, mid-clavicular line. Displaced (left ventricular dilatation), heaving (hypertrophy), tapping (mitral stenosis).",
"Auscultation: heart sounds S1, S2, extra sounds (S3 — ventricular failure; S4 — hypertensive heart disease), murmurs.",
"Breasts: must be examined in women and men (gynaecomastia = liver disease, drugs, hypogonadism).",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("8.4 Abdomen"))
story.append(P(
"The abdominal examination must be preceded by a detailed general examination. "
"The patient should lie flat with hips and knees extended (later flexed for palpation)."
))
for item in [
"Inspection: distension, visible peristalsis, pulsation, scars, dilated veins (caput medusae), hernial orifices, stoma.",
"Grey Turner's sign: flank bruising — retroperitoneal haemorrhage (severe pancreatitis, leaking AAA).",
"Cullen's sign: periumbilical bruising — severe pancreatitis, ruptured ectopic pregnancy, liver trauma.",
"Palpation: tenderness, guarding, rigidity, organomegaly (liver, spleen, kidneys), masses.",
"Murphy's sign: sudden arrest of inspiration on deep palpation of RUQ — acute cholecystitis.",
"Percussion: liver span, splenic dullness, shifting dullness (ascites), fluid thrill.",
"Auscultation: bowel sounds, bruits (aorta, renal arteries), rub (peritonitis over organ).",
"Hernial orifices: inguinal (direct vs. indirect), femoral, umbilical, incisional.",
"Rectal examination: must not be omitted — prostatic enlargement, rectal masses, blood, prostate carcinoma.",
"Genitalia: testicular examination — torsion, epididymo-orchitis, tumours.",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("8.5 Lower Limbs"))
for item in [
"Vascular: femoral, popliteal, dorsalis pedis, posterior tibial pulses; ankle-brachial pressure index (ABPI) if vascular disease suspected.",
"Neurological: power, tone, reflexes (knee L3–4, ankle S1), plantar response, sensation (light touch, pin-prick, vibration, proprioception).",
"Varicose veins: distribution, skin changes (eczema, lipodermatosclerosis, ulceration), Trendelenburg test.",
"Oedema: pitting (cardiac, renal, hepatic, nutritional) vs. non-pitting (lymphoedema, myxoedema).",
"Joints: hip, knee, ankle — ROM, deformity, effusion, crepitus.",
]:
story.append(B(item))
story.append(PageBreak())
# ========== SECTION 9: RED FLAG SIGNS ==========
story.append(section_header("9. RED FLAG SIGNS"))
story.append(SP(0.3))
story.append(warning_box("Red Flags Requiring Urgent Investigation / Referral", [
"Unexplained weight loss (>10% body weight in 6 months) — malignancy, TB, HIV, endocrine disease.",
"Haemoptysis — pulmonary carcinoma, TB, bronchiectasis, PE.",
"Haematemesis or malaena — upper GI bleeding (peptic ulcer, varices, carcinoma).",
"Rectal bleeding — colorectal carcinoma, IBD, angiodysplasia.",
"Persistent dysphagia — oesophageal carcinoma, achalasia, extrinsic compression.",
"Unexplained dyspnoea at rest — cardiac failure, massive PE, pneumonia, pleural effusion.",
"New neurological deficit — stroke, cord compression, meningitis, intracranial mass.",
"Sudden severe headache ('thunderclap') — subarachnoid haemorrhage.",
"Severe abdominal pain with rigidity — peritonitis, ruptured viscus, leaking aneurysm.",
"High fever >39°C with altered consciousness — septic encephalopathy, meningitis.",
"Progressive bone pain + hypercalcaemia — multiple myeloma, bony metastases.",
"Left supraclavicular lymphadenopathy (Troisier's sign) — intra-abdominal malignancy.",
"Virchow's triad (stasis, hypercoagulability, vessel injury) — deep vein thrombosis / PE.",
]))
story.append(SP(0.3))
story.append(info_box("Red Flags in Spinal Examination (from Goldman-Cecil Medicine)", [
"History of cancer, unexplained weight loss — vertebral metastasis.",
"Fever, IV drug use, recent infection — vertebral osteomyelitis / discitis.",
"Recent significant trauma — fracture.",
"Bowel or bladder dysfunction with back pain — cauda equina syndrome (surgical emergency).",
"Progressive neurological deficit — cord compression.",
"Night pain / rest pain not relieved by position change — malignancy / infection.",
"Prolonged steroid use — osteoporotic fracture risk.",
], color=WARN_ORANGE, bg=WARN_BG, bullet_char="⚠"))
story.append(PageBreak())
# ========== SECTION 10: INVESTIGATIONS ==========
story.append(section_header("10. PROVISIONAL DIAGNOSIS AND SPECIAL INVESTIGATIONS"))
story.append(SP(0.3))
story.append(P(
"At this stage of the clinical encounter, the clinician should be able to formulate a "
"<b>provisional (working) diagnosis</b> and a list of <b>differential diagnoses</b>. "
"A useful maxim: <i>'If you diagnose a rare disease, you will be rarely correct'</i> — "
"common conditions should be considered first."
))
story.append(SP(0.2))
inv_data = [
["Full Blood Count (FBC)",
"WBC (infection/leukaemia), Hb (anaemia), platelets (haematological disorders).",
"All patients; especially inflammatory swellings (leukocytosis, increased polymorphs), chronic disease (low Hb, high ESR)"],
["ESR / CRP",
"Elevated in infection, inflammation, malignancy.",
"Chronic inflammatory swellings, malignancy (ESR often markedly elevated)"],
["Blood Glucose",
"Diabetes mellitus.",
"Recurrent abscesses, carbuncles, poor wound healing — always check glucose"],
["Urea, Creatinine, Electrolytes",
"Renal function.",
"Oedema, hypertension, altered urine output, pre-operative assessment"],
["Liver Function Tests (LFTs)",
"Bilirubin, AST, ALT, ALP, albumin.",
"Jaundice, hepatomegaly, alcohol history, malignancy"],
["Serology / Special Tests",
"VDRL/RPR (syphilis), HIV serology, hepatitis B & C, Weil-Felix.",
"Suspected STI, condyloma, gumma, syphilitic lesions"],
["Urine Analysis",
"Glucose, protein, blood, microscopy.",
"Recurrent infections (glucose), renal disease (protein/blood)"],
["Fine Needle Aspiration Biopsy (FNAB)",
"Cytological diagnosis from cystic or solid swelling.",
"Thyroid nodule, lymph node, breast lump, soft tissue mass — safest initial biopsy"],
["Aspiration and fluid analysis",
"Physical, chemical, microscopic, bacteriological.",
"Chronic cystic swellings, joint effusions"],
["Imaging (X-ray, USS, CT, MRI)",
"Morphology, extent, vascularity, invasion.",
"As directed by provisional diagnosis"],
]
story.append(make_table(["Investigation", "What It Assesses", "When to Order"],
inv_data, col_widths=[4.0*cm, 5.0*cm, BODY_W - 9.0*cm]))
story.append(PageBreak())
# ========== SECTION 11: CLINICAL DIAGNOSIS ==========
story.append(section_header("11. CLINICAL DIAGNOSIS AND TREATMENT PLAN"))
story.append(SP(0.3))
story.append(P(
"A <b>clinical diagnosis</b> is more than simply naming the organ involved. It must identify:"
))
for item in [
"The affected organ and the exact site (e.g., carcinoma of the <i>right upper outer quadrant</i> of the breast).",
"The nature of the pathological process (inflammatory, neoplastic, degenerative, congenital, traumatic, vascular).",
"The extent and stage of the disease (e.g., T2N1M0 carcinoma breast).",
"Structures involved in spread or metastasis (lymph nodes, adjacent organs, distant sites).",
"For hernia: direct or indirect; reducible or irreducible; contents (intestine, omentum, bladder).",
"Complications already present (obstruction, strangulation, perforation, haemorrhage).",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("Treatment Planning"))
story.append(P(
"After clinical diagnosis is established, the treatment plan is formulated:"
))
treat_data = [
["Medical Treatment", "Drug therapy, dietary modification, physiotherapy, conservative management."],
["Surgical Treatment", "Indicates the procedure, approach (open/laparoscopic/endoscopic), anaesthetic type, timing (elective/urgent/emergency)."],
["Pre-operative Preparation", "Optimise co-morbidities, correct anaemia, control diabetes, stop anticoagulants, NBM instructions, consent."],
["Intra-operative Phase", "Anaesthesia, procedure, intraoperative findings documented."],
["Post-operative Period", "Monitoring, analgesia, fluid management, wound care, prophylactic antibiotics, DVT prophylaxis."],
["Follow-Up", "Outpatient review, histology results, adjuvant therapy, surveillance imaging."],
]
story.append(make_table(["Phase", "Key Actions"], treat_data, col_widths=[4.5*cm, BODY_W - 4.5*cm]))
story.append(PageBreak())
# ========== SECTION 12: SUMMARY TABLE ==========
story.append(section_header("12. SUMMARY TABLE — KEY CLINICAL SIGNS AT A GLANCE"))
story.append(SP(0.3))
summary_data = [
["Pallor", "Conjunctiva, nail bed, palmar creases", "Anaemia, haemorrhage, shock"],
["Central cyanosis", "Tongue, lips, oral mucosa", "Lung disease, R→L shunt, severe cardiac failure"],
["Peripheral cyanosis", "Nail beds, fingertips, toes, nose tip", "Reduced peripheral flow, cold, shock"],
["Jaundice", "Sclera (first), skin, mucous membranes", "Pre-hepatic, hepatic, post-hepatic"],
["Clubbing", "Fingers (obliteration of nail angle)", "Lung Ca, bronchiectasis, cyanotic CHD, cirrhosis"],
["Koilonychia", "Spoon-shaped nails", "Iron deficiency anaemia"],
["Splinter haemorrhages", "Under nail (linear dark streaks)", "Infective endocarditis, vasculitis"],
["Leukonychia", "White nails", "Hypoalbuminaemia, cirrhosis"],
["Xanthelasma", "Yellow eyelid plaques", "Hyperlipidaemia"],
["Kayser-Fleischer ring", "Corneal golden ring", "Wilson's disease"],
["Virchow's node", "Left supraclavicular lymph node", "Intra-abdominal malignancy (Troisier's sign)"],
["Cullen's sign", "Periumbilical bruising", "Severe pancreatitis, ruptured ectopic"],
["Grey Turner's sign", "Flank bruising", "Retroperitoneal haemorrhage, pancreatitis"],
["Murphy's sign", "Pain arrest on deep RUQ palpation in inspiration", "Acute cholecystitis"],
["Shifting dullness", "Dullness shifts with position on percussion", "Ascites (>500 mL)"],
["JVP elevation", "Visible distension of neck veins", "Right heart failure, SVC obstruction, tamponade"],
["Pulsus paradoxus", "Pulse volume decreases on inspiration", "Cardiac tamponade, severe asthma"],
["Caput medusae", "Dilated periumbilical veins", "Portal hypertension (liver cirrhosis)"],
["Gynaecomastia", "Enlarged male breast tissue", "Cirrhosis, drugs, hypogonadism, oestrogen-producing tumour"],
["Facies hippocratica", "Sunken face, hollow eyes", "Generalised peritonitis, terminal state"],
["Risus sardonicus", "Fixed grin", "Tetanus"],
]
story.append(make_table(["Clinical Sign", "Where to Look", "Associated Condition"], summary_data,
col_widths=[4.5*cm, 5.0*cm, BODY_W - 9.5*cm]))
story.append(SP(0.4))
story.append(HR())
story.append(P(
"<b>Sources:</b> S Das — A Manual on Clinical Surgery, 13th Edition | Bailey and Love's Short Practice of Surgery, "
"28th Edition | Goldman-Cecil Medicine, International Edition | Bradley and Daroff's Neurology in Clinical Practice."
))
# ===================== BUILD DOC =====================
doc.build(story)
print("PDF generated successfully:", OUTPUT_PATH)
Failed to create session: unauthorized: authentication failed: Bearer token is invalid
We encountered an error while processing your request. Please try again or reach out to us for help.
Continue
mkdir -p /home/daytona/workspace/clinical-examination && echo "ready"
~/clinical-examination/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib.colors import HexColor, black, white
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether, NextPageTemplate
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
OUTPUT_PATH = "/home/daytona/workspace/clinical-examination/Approach_to_Patient_General_Examination.pdf"
# Color palette
DARK_BLUE = HexColor("#1A3C6B")
MED_BLUE = HexColor("#2E6DA4")
LIGHT_BLUE = HexColor("#D6E8F7")
ACCENT_TEAL = HexColor("#1A8F8F")
ACCENT_GOLD = HexColor("#C9820A")
TABLE_HEADER = HexColor("#1A3C6B")
TABLE_ALT = HexColor("#EEF4FB")
TABLE_EVEN = HexColor("#FAFCFF")
BORDER_GRAY = HexColor("#C0C8D4")
WARN_ORANGE = HexColor("#E85C0D")
WARN_BG = HexColor("#FFF3E0")
BOX_GREEN = HexColor("#1F7A4F")
BOX_GREEN_BG = HexColor("#E8F5EE")
LIGHT_GRAY = HexColor("#F5F5F5")
PAGE_W, PAGE_H = A4
LEFT_M = 2.0*cm
RIGHT_M = 2.0*cm
TOP_M = 2.5*cm
BOT_M = 2.2*cm
BODY_W = PAGE_W - LEFT_M - RIGHT_M
# ---- PAGE DECORATIONS ----
def draw_page_decorations(canv, doc):
canv.saveState()
canv.setFillColor(DARK_BLUE)
canv.rect(0, PAGE_H - 1.0*cm, PAGE_W, 1.0*cm, fill=1, stroke=0)
canv.setFillColor(white)
canv.setFont("Helvetica-Bold", 8)
canv.drawString(LEFT_M, PAGE_H - 0.68*cm, "APPROACH TO THE PATIENT & GENERAL EXAMINATION")
canv.setFont("Helvetica", 8)
canv.drawRightString(PAGE_W - RIGHT_M, PAGE_H - 0.68*cm, f"Page {doc.page}")
canv.setFillColor(DARK_BLUE)
canv.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
canv.setFillColor(white)
canv.setFont("Helvetica", 7)
canv.drawCentredString(PAGE_W/2, 0.22*cm,
"Clinical Reference — S Das Manual on Clinical Surgery | Bailey & Love | Goldman-Cecil Medicine")
canv.restoreState()
def draw_cover(canv, doc):
canv.saveState()
canv.setFillColor(DARK_BLUE)
canv.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
# diagonal stripe
canv.setFillColor(HexColor("#1E4A80"))
p = canv.beginPath()
p.moveTo(0, PAGE_H*0.45); p.lineTo(PAGE_W, PAGE_H*0.35)
p.lineTo(PAGE_W, PAGE_H*0.55); p.lineTo(0, PAGE_H*0.65)
p.close()
canv.drawPath(p, fill=1, stroke=0)
# white panel
canv.setFillColor(white)
canv.roundRect(LEFT_M, PAGE_H*0.22, BODY_W, PAGE_H*0.56, 10, fill=1, stroke=0)
# gold accent bar at top of panel
canv.setFillColor(ACCENT_GOLD)
panel_top = PAGE_H*0.22 + PAGE_H*0.56
canv.rect(LEFT_M, panel_top - 10, BODY_W, 10, fill=1, stroke=0)
# title
canv.setFillColor(DARK_BLUE)
canv.setFont("Helvetica-Bold", 24)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.685, "APPROACH TO THE PATIENT")
canv.setFont("Helvetica-Bold", 18)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.635, "AND GENERAL EXAMINATION")
# rule
canv.setStrokeColor(ACCENT_GOLD)
canv.setLineWidth(2)
canv.line(LEFT_M+2*cm, PAGE_H*0.608, PAGE_W-LEFT_M-2*cm, PAGE_H*0.608)
# subtitle
canv.setFillColor(MED_BLUE)
canv.setFont("Helvetica-Bold", 11)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.578, "A Comprehensive Clinical Reference Guide")
# topics
topics = [
"History Taking | Physical Examination | General Survey",
"Pallor | Cyanosis | Jaundice | Oedema | Vital Signs",
"Systemic Examination | Red Flag Signs | Clinical Diagnosis"
]
canv.setFont("Helvetica", 9)
canv.setFillColor(HexColor("#444444"))
y = PAGE_H*0.54
for t in topics:
canv.drawCentredString(PAGE_W/2, y, t)
y -= 0.45*cm
# source badge
canv.setFillColor(LIGHT_BLUE)
canv.roundRect(LEFT_M+2.5*cm, PAGE_H*0.255, BODY_W-5*cm, 0.9*cm, 5, fill=1, stroke=0)
canv.setFillColor(DARK_BLUE)
canv.setFont("Helvetica-Bold", 7.5)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.278,
"Sources: S Das Manual on Clinical Surgery | Bailey & Love's Surgery | Goldman-Cecil Medicine")
# date
canv.setFillColor(white)
canv.setFont("Helvetica", 9)
canv.drawCentredString(PAGE_W/2, PAGE_H*0.175, "Prepared: July 2026")
canv.restoreState()
# ---- STYLES ----
h1_s = ParagraphStyle("H1", fontName="Helvetica-Bold", fontSize=15, textColor=white,
spaceAfter=2, spaceBefore=4, leading=19)
h2_s = ParagraphStyle("H2", fontName="Helvetica-Bold", fontSize=12, textColor=DARK_BLUE,
spaceAfter=4, spaceBefore=12, leading=16)
h3_s = ParagraphStyle("H3", fontName="Helvetica-Bold", fontSize=10.5, textColor=ACCENT_TEAL,
spaceAfter=3, spaceBefore=8, leading=14)
h4_s = ParagraphStyle("H4", fontName="Helvetica-Bold", fontSize=10, textColor=MED_BLUE,
spaceAfter=2, spaceBefore=5, leading=13)
body_s = ParagraphStyle("Body", fontName="Helvetica", fontSize=9.5, textColor=black,
spaceAfter=5, spaceBefore=2, leading=14, alignment=TA_JUSTIFY)
bullet_s = ParagraphStyle("Bullet", fontName="Helvetica", fontSize=9.5, textColor=black,
spaceAfter=3, spaceBefore=1, leading=13, leftIndent=14, bulletIndent=4)
sub_s = ParagraphStyle("Sub", fontName="Helvetica", fontSize=9, textColor=HexColor("#333333"),
spaceAfter=2, spaceBefore=1, leading=12, leftIndent=28, bulletIndent=18)
box_s = ParagraphStyle("Box", fontName="Helvetica", fontSize=9.5, textColor=black,
spaceAfter=3, spaceBefore=1, leading=13, leftIndent=6)
warn_s = ParagraphStyle("Warn", fontName="Helvetica", fontSize=9.5,
textColor=HexColor("#7B2800"),
spaceAfter=3, spaceBefore=1, leading=13, leftIndent=6)
toc_main = ParagraphStyle("TocMain", fontName="Helvetica-Bold", fontSize=10, textColor=DARK_BLUE,
spaceAfter=3, spaceBefore=5, leading=14, leftIndent=0)
toc_sub = ParagraphStyle("TocSub", fontName="Helvetica", fontSize=9.5, textColor=DARK_BLUE,
spaceAfter=2, spaceBefore=1, leading=13, leftIndent=16)
def sec(title):
data = [[Paragraph(title, h1_s)]]
t = Table(data, colWidths=[BODY_W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), MED_BLUE),
("LEFTPADDING", (0,0),(-1,-1), 10),
("RIGHTPADDING", (0,0),(-1,-1), 10),
("TOPPADDING", (0,0),(-1,-1), 7),
("BOTTOMPADDING", (0,0),(-1,-1), 7),
]))
return t
def info_box(title, items, color=BOX_GREEN, bg=BOX_GREEN_BG, bullet_char="✔"):
rows = [[Paragraph(f"<b>{title}</b>",
ParagraphStyle("BT", fontName="Helvetica-Bold", fontSize=10,
textColor=color, spaceAfter=3, leading=14, leftIndent=6))]]
for item in items:
rows.append([Paragraph(f"{bullet_char} {item}", box_s)])
t = Table(rows, colWidths=[BODY_W - 1.2*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), bg),
("BOX", (0,0),(-1,-1), 1, color),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
]))
return t
def warn_box(title, items):
rows = [[Paragraph(f"⚠ {title}",
ParagraphStyle("WT", fontName="Helvetica-Bold", fontSize=10,
textColor=WARN_ORANGE, spaceAfter=3, leading=14, leftIndent=6))]]
for item in items:
rows.append([Paragraph(f"• {item}", warn_s)])
t = Table(rows, colWidths=[BODY_W - 1.2*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), WARN_BG),
("BOX", (0,0),(-1,-1), 1.5, WARN_ORANGE),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
]))
return t
def mktable(headers, rows, col_widths=None):
if col_widths is None:
col_widths = [BODY_W / len(headers)] * len(headers)
th_style = ParagraphStyle("TH", fontName="Helvetica-Bold", fontSize=8.5,
textColor=white, leading=12, leftIndent=2)
td_style = ParagraphStyle("TD", fontName="Helvetica", fontSize=8.5,
textColor=black, leading=12, leftIndent=2)
data = [[Paragraph(h, th_style) for h in headers]]
for row in rows:
data.append([Paragraph(str(c), td_style) for c in row])
t = Table(data, colWidths=col_widths, repeatRows=1)
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), TABLE_HEADER),
("ROWBACKGROUNDS",(0,1),(-1,-1),[TABLE_EVEN, TABLE_ALT]),
("ALIGN", (0,0),(-1,-1), "LEFT"),
("VALIGN", (0,0),(-1,-1), "TOP"),
("BOX", (0,0),(-1,-1), 0.8, BORDER_GRAY),
("INNERGRID", (0,0),(-1,-1), 0.4, BORDER_GRAY),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
]))
return t
def B(text): return Paragraph(f"• {text}", bullet_s)
def SB(text): return Paragraph(f"– {text}", sub_s)
def P(text): return Paragraph(text, body_s)
def H2(text): return Paragraph(text, h2_s)
def H3(text): return Paragraph(text, h3_s)
def H4(text): return Paragraph(text, h4_s)
def SP(h=0.2): return Spacer(1, h*cm)
def HR(): return HRFlowable(width="100%", thickness=0.5, color=BORDER_GRAY, spaceAfter=4, spaceBefore=4)
# ===================== BUILD =====================
doc = BaseDocTemplate(
OUTPUT_PATH, pagesize=A4,
leftMargin=LEFT_M, rightMargin=RIGHT_M,
topMargin=TOP_M + 0.5*cm, bottomMargin=BOT_M + 0.5*cm,
title="Approach to the Patient and General Examination",
author="Clinical Reference", subject="Clinical Medicine",
)
cover_frame = Frame(0, 0, PAGE_W, PAGE_H, id="cover")
content_frame = Frame(LEFT_M, BOT_M+0.7*cm, BODY_W,
PAGE_H - TOP_M - BOT_M - 1.5*cm, id="content")
doc.addPageTemplates([
PageTemplate(id="cover", frames=[cover_frame], onPage=draw_cover),
PageTemplate(id="content", frames=[content_frame], onPage=draw_page_decorations),
])
story = []
story.append(Spacer(1, PAGE_H)) # cover filled by draw_cover callback
story.append(NextPageTemplate("content"))
story.append(PageBreak())
# ============================================================
# TABLE OF CONTENTS
# ============================================================
story.append(sec("TABLE OF CONTENTS"))
story.append(SP(0.4))
toc = [
("1", "Approach to the Patient — Overview", toc_main),
("2", "History Taking", toc_main),
("2.1", "Patient Particulars", toc_sub),
("2.2", "Chief Complaints", toc_sub),
("2.3", "History of Present Illness (SOCRATES)", toc_sub),
("2.4", "Past, Family and Personal History", toc_sub),
("3", "Physical Examination — Framework", toc_main),
("4", "General Survey", toc_main),
("4.1", "General Assessment of Illness", toc_sub),
("4.2", "Mental State and Level of Consciousness", toc_sub),
("4.3", "Nutritional Status and Body Build", toc_sub),
("4.4", "Posture, Attitude, and Decubitus", toc_sub),
("4.5", "Facies", toc_sub),
("5", "Colour of the Skin — Pallor, Cyanosis, Jaundice", toc_main),
("5.1", "Pallor", toc_sub),
("5.2", "Cyanosis", toc_sub),
("5.3", "Jaundice", toc_sub),
("6", "Vital Signs", toc_main),
("6.1", "Pulse", toc_sub),
("6.2", "Blood Pressure", toc_sub),
("6.3", "Temperature", toc_sub),
("6.4", "Respiratory Rate", toc_sub),
("7", "Peripheral Signs — Hands, Nails, Eyes, Lymph Nodes", toc_main),
("8", "Systemic Examination Checklist", toc_main),
("8.1", "Head and Neck", toc_sub),
("8.2", "Upper Limbs", toc_sub),
("8.3", "Thorax (Respiratory & Cardiovascular)", toc_sub),
("8.4", "Abdomen", toc_sub),
("8.5", "Lower Limbs", toc_sub),
("9", "Red Flag Signs", toc_main),
("10", "Special Investigations", toc_main),
("11", "Clinical Diagnosis and Treatment Plan", toc_main),
("12", "Summary Table — Key Clinical Signs at a Glance", toc_main),
]
for num, title, style in toc:
story.append(Paragraph(f"{num}. {title}", style))
story.append(PageBreak())
# ============================================================
# 1. OVERVIEW
# ============================================================
story.append(sec("1. APPROACH TO THE PATIENT — OVERVIEW"))
story.append(SP(0.3))
story.append(P(
"The approach to the patient is the foundation of clinical medicine. A good clinician begins "
"observing the patient from the moment they enter the consulting room — noting their gait, "
"posture, facial expression, breathing pattern, and apparent level of distress. The student "
"should learn a systematic scheme and make it a reflex, applicable to every patient."
))
story.append(SP(0.2))
story.append(info_box("The General Clinical Scheme", [
"1. History Taking",
"2. Physical Examination (General Survey + Local Examination + Systemic Examination)",
"3. Special Investigations",
"4. Provisional and Clinical Diagnosis",
"5. Treatment — Medical and/or Surgical",
"6. Progress Monitoring (Post-operative / Post-treatment period)",
"7. Follow-Up",
"8. Termination / Discharge / Outcome",
]))
story.append(SP(0.3))
story.append(P(
"It is helpful to have the accompanying relative or carer present during history-taking. "
"They can provide valuable information about the type of injury, changes in behaviour, or "
"social circumstances the patient may not relay accurately. Always begin the consultation by "
"addressing the patient by name — this builds rapport and psychological confidence."
))
story.append(PageBreak())
# ============================================================
# 2. HISTORY TAKING
# ============================================================
story.append(sec("2. HISTORY TAKING"))
story.append(SP(0.3))
story.append(H2("2.1 Patient Particulars"))
story.append(P("Before asking about complaints, document the following:"))
story.append(mktable(
["Parameter", "Clinical Significance"],
[
["Name", "Builds rapport; aids identification; psychological benefit pre- and post-operatively."],
["Age", "Congenital anomalies — birth/early life; sarcomas — teenagers; carcinomas — >40 yrs; BPH — old age; Wilms' tumour — infants."],
["Sex", "Thyroid disease, visceroptosis, cystitis — commoner in females. Gastric/lung carcinoma — commoner in males. Haemophilia — males only."],
["Occupation", "Exposure to toxins (chemical, biological, physical); asbestos (mesothelioma); coal dust (pneumoconiosis)."],
["Religion / Address", "Geographic disease prevalence; dietary norms; enables follow-up contact."],
["Date of Admission", "Timeline; medico-legal documentation."],
],
col_widths=[3.8*cm, BODY_W - 3.8*cm]
))
story.append(SP(0.3))
story.append(H2("2.2 Chief Complaints"))
story.append(P(
"Record in the patient's own words — do not translate into medical terminology yet. "
"List complaints in chronological order (earliest first). Each must include its "
"<b>duration</b>. Example: <i>'Neck swelling — 3 months; pain — 2 weeks; dysphagia — 1 week.'</i>"
))
story.append(SP(0.2))
story.append(H2("2.3 History of Present Illness (HPI) — SOCRATES Framework"))
story.append(mktable(
["Dimension", "Key Questions to Ask"],
[
["Site", "Where exactly is the problem? Ask patient to point with one finger."],
["Onset", "When did it start? Was onset sudden (minutes) or gradual (days/weeks/months)?"],
["Character", "Describe quality — sharp, dull, burning, throbbing, colicky, tearing, pressure-like."],
["Radiation / Spread", "Does pain/swelling radiate or spread to another area?"],
["Associations", "Any fever, weight loss, night sweats, vomiting, dysphagia, dyspnoea, bleeding?"],
["Time course", "Constant or intermittent? Progressive, static, or improving? Diurnal variation?"],
["Exacerbating / Relieving", "Effect of food, posture, activity, medications, rest, heat/cold?"],
["Severity", "Pain scale 0–10; functional limitation; impact on daily activities and sleep."],
],
col_widths=[3.5*cm, BODY_W - 3.5*cm]
))
story.append(SP(0.3))
story.append(H2("2.4 Past, Family and Personal History"))
story.append(H4("Past Medical/Surgical History"))
story.append(P(
"Ask about: previous illnesses, surgeries, hospitalisations, blood transfusions, allergies "
"(especially drugs, penicillin, iodine/contrast), and current medications. "
"Recording all previous operations with dates is a practice that prevents many catastrophes."
))
story.append(H4("Family History"))
story.append(P(
"Many diseases recur in families: haemophilia, tuberculosis, diabetes mellitus, essential "
"hypertension, peptic ulcer, majority of cancers (particularly breast cancer), fissure-in-ano, "
"and piles. Ask about parents' health/cause of death, siblings, and children."
))
story.append(H4("Personal History"))
story.append(P(
"Smoking (type, quantity, pack-years); alcohol (quantity, frequency, units/week); diet; "
"marital status. In <b>women</b>: menstrual history (LMP, regularity, dysmenorrhoea, flow), "
"number of pregnancies, deliveries, miscarriages, Caesarean sections, vaginal discharge."
))
story.append(PageBreak())
# ============================================================
# 3. PHYSICAL EXAMINATION FRAMEWORK
# ============================================================
story.append(sec("3. PHYSICAL EXAMINATION — FRAMEWORK"))
story.append(SP(0.3))
story.append(P(
"Physical examination uses the systematic sequence: "
"<b>Inspection → Palpation → Percussion → Auscultation</b>. "
"It is divided into three main components:"
))
story.append(SP(0.2))
story.append(mktable(
["Component", "Description", "Includes"],
[
["A. General Survey",
"Assessment of the patient as a whole",
"Mental state, build, nutrition, posture, skin colour, pulse, temperature, respiration"],
["B. Local Examination",
"Focused examination of the presenting complaint / affected region",
"Inspection, palpation, percussion, auscultation of the local area"],
["C. Systemic Examination",
"Examination of ALL body systems regardless of presenting complaint",
"Head & neck, limbs, thorax, abdomen, genitalia, nervous system"],
],
col_widths=[3.5*cm, 4.8*cm, BODY_W - 8.3*cm]
))
story.append(SP(0.3))
story.append(info_box("Environment Requirements for Examination", [
"Perform in good natural daylight — artificial light may miss faint jaundice.",
"Patient should remove all clothing and be covered only by a dressing gown.",
"For female patients, an attendant nurse must always be present.",
"Begin observing the patient as they enter the clinic (gait, posture, distress).",
"Ensure patient is positioned correctly for each component of examination.",
"Warm hands before palpation; explain each step to the patient.",
], color=MED_BLUE, bg=LIGHT_BLUE))
story.append(PageBreak())
# ============================================================
# 4. GENERAL SURVEY
# ============================================================
story.append(sec("4. GENERAL SURVEY"))
story.append(SP(0.3))
story.append(H2("4.1 General Assessment of Illness"))
story.append(P(
"This is the <b>first and most critical step</b>. Assess whether the patient looks well, ill, or "
"critically ill. In <b>severely ill patients</b>, rapidly move to key local examination and "
"institute emergency treatment without delay."
))
story.append(SP(0.2))
story.append(mktable(
["Grade", "Appearance", "Clinical Implication"],
[
["Well", "Alert, comfortable, no distress", "Non-urgent; full structured examination can proceed."],
["Ill (mild–moderate)", "Looks unwell, some distress, cooperative", "Focused examination; prioritise key findings."],
["Very ill / Critically ill", "Severe distress, pale, tachycardic, hypotensive, sweaty", "Rapid assessment; initiate resuscitation concurrently."],
["Moribund", "Barely responsive, end-stage appearance", "Immediate palliative or resuscitative measures; senior review."],
],
col_widths=[3.0*cm, 5.5*cm, BODY_W - 8.5*cm]
))
story.append(SP(0.3))
story.append(H2("4.2 Mental State and Level of Consciousness"))
story.append(P(
"Particularly important in: head injury, metabolic encephalopathy, drug overdose, "
"sepsis, and any critically ill patient."
))
story.append(SP(0.15))
story.append(P("<b>Five Stages of Level of Consciousness (Clinical Staging):</b>"))
for item in [
"Stage 1 — Fully conscious: Perfect orientation to time, place, and person.",
"Stage 2 — Confused: Oriented but responds slowly; disoriented to time.",
"Stage 3 — Stuporous / Semi-conscious: Responds to verbal commands; abnormal movements may be present.",
"Stage 4 — Unconscious but responding to painful stimuli (localises or withdraws).",
"Stage 5 — Deeply unconscious: No response to any stimuli whatsoever.",
]:
story.append(B(item))
story.append(SP(0.2))
story.append(info_box("Glasgow Coma Scale (GCS)", [
"Eye Opening (E): Spontaneous=4, To voice=3, To pain=2, None=1",
"Verbal Response (V): Oriented=5, Confused=4, Words=3, Sounds=2, None=1",
"Motor Response (M): Obeys=6, Localises=5, Withdraws=4, Flexion=3, Extension=2, None=1",
"Total Score: 3–15. Mild TBI: 13–15 | Moderate: 9–12 | Severe: ≤8 (secure airway!)",
], color=MED_BLUE, bg=LIGHT_BLUE))
story.append(SP(0.3))
story.append(H2("4.3 Nutritional Status and Body Build"))
story.append(mktable(
["Assessment", "Features", "Clinical Significance"],
[
["Well-nourished", "Normal BMI (18.5–24.9), good muscle bulk", "Baseline normal."],
["Overweight / Obese", "BMI >25 / >30, excess adipose tissue, central adiposity", "Risk: DM, CVD, OA, OSA, surgical wound complications."],
["Underweight / Malnourished", "BMI <18.5, visible bony prominences, hollow cheeks", "Malabsorption, malignancy, chronic infection, anorexia."],
["Cachexia", "Severe wasting, temporal hollowing, sunken eyes, loose skin", "Malignancy, AIDS, severe chronic disease, TB, advanced CKD."],
["Cushing's body habitus", "Central obesity, buffalo hump, moon face, purple striae", "Cushing's syndrome (endogenous/exogenous steroids)."],
],
col_widths=[3.8*cm, 5.2*cm, BODY_W - 9.0*cm]
))
story.append(SP(0.3))
story.append(H2("4.4 Posture, Attitude, and Decubitus"))
story.append(P(
"<b>Decubitus</b> = the position adopted by the patient in bed. Informative in many conditions:"
))
story.append(mktable(
["Position / Posture", "Associated Condition"],
[
["Curled on side away from light", "Cerebral irritation, meningitis"],
["Lying completely still (avoids movement)", "Generalised peritonitis"],
["Unable to lie flat (orthopnoea)", "Left heart failure, severe asthma, COPD"],
["Leaning forward (tripod position)", "Acute pericarditis, severe respiratory distress"],
["Opisthotonos (arching backwards, rigid)", "Tetanus, severe bacterial meningitis"],
["Decorticate posturing (arms flexed)", "Bilateral cortical damage above midbrain"],
["Decerebrate posturing (arms extended, legs stiff)", "Brainstem injury — grave prognosis"],
["Knees drawn up to abdomen", "Pancreatitis, peritonitis (reduces peritoneal stretch)"],
],
col_widths=[6.0*cm, BODY_W - 6.0*cm]
))
story.append(SP(0.3))
story.append(H2("4.5 Facies"))
story.append(P(
"The face is the 'mirror of the mind'. Characteristic facies allow rapid clinical diagnosis "
"and must be recognised by every clinician:"
))
story.append(mktable(
["Facies Type", "Features", "Associated Condition"],
[
["Facies hippocratica", "Sunken eyes, pale, hollow cheeks, cold clammy skin", "Generalised peritonitis, terminal illness"],
["Risus sardonicus", "Fixed grin from sustained facial muscle spasm", "Tetanus"],
["Mask face (expressionless)", "Reduced blinking, fixed stare, reduced facial movement", "Parkinsonism"],
["Moon face", "Round, plethoric, full face", "Cushing's syndrome, long-term steroid therapy"],
["Adenoid facies", "Open mouth, upturned nose, elongated face", "Hypertrophied adenoids, chronic mouth breathing"],
["Acromegalic facies", "Prominent supraorbital ridge, prognathism, large nose", "Acromegaly (GH excess)"],
["Myxoedema facies", "Puffy, dry, thickened skin; periorbital oedema; loss of outer 1/3 eyebrow", "Hypothyroidism"],
["Thyrotoxic facies", "Exophthalmos, lid lag, anxious, wide-eyed expression", "Hyperthyroidism — Graves' disease"],
["Leonine facies", "Coarse thickened skin, loss of eyebrows, nodular infiltration", "Lepromatous leprosy"],
["Paget's facies", "Enlarged skull, frontal bossing", "Paget's disease of bone"],
],
col_widths=[4.0*cm, 5.5*cm, BODY_W - 9.5*cm]
))
story.append(PageBreak())
# ============================================================
# 5. SKIN COLOUR
# ============================================================
story.append(sec("5. COLOUR OF THE SKIN — PALLOR, CYANOSIS, JAUNDICE"))
story.append(SP(0.3))
story.append(H2("5.1 Pallor"))
story.append(P(
"Pallor (unusual paleness) results from reduced blood flow to superficial vessels or "
"reduced haemoglobin. Seen in: <b>anaemia, haemorrhage, shock, intense emotion, and vasospasm</b>."
))
story.append(SP(0.1))
story.append(P("<b>Sites to examine for pallor:</b>"))
for s in [
"Lower palpebral conjunctiva — most reliable site; pale pink or white indicates anaemia.",
"Mucous membranes of lips and buccal mucosa.",
"Nail beds.",
"Palmar creases — pallor of the creases correlates with Hb <7 g/dL.",
]:
story.append(B(s))
story.append(SP(0.3))
story.append(H2("5.2 Cyanosis"))
story.append(P(
"Cyanosis is the bluish/purplish discolouration of skin and mucous membranes due to "
"<b>excess reduced (deoxygenated) haemoglobin</b>. Requires a minimum of <b>5 g/dL of "
"reduced Hb</b> to be visible — therefore <b>not detectable in severe anaemia</b>."
))
story.append(SP(0.15))
td = ParagraphStyle("TD2", fontName="Helvetica", fontSize=8.5, textColor=black, leading=12, leftIndent=2)
th = ParagraphStyle("TH2", fontName="Helvetica-Bold", fontSize=8.5, textColor=white, leading=12, leftIndent=2)
tb = ParagraphStyle("THL", fontName="Helvetica-Bold", fontSize=8.5, textColor=DARK_BLUE, leading=12,
leftIndent=2, backColor=LIGHT_BLUE)
cyan_data = [
[Paragraph("", th), Paragraph("Peripheral Cyanosis", th), Paragraph("Central Cyanosis", th)],
[Paragraph("<b>Mechanism</b>", tb), Paragraph("Excessive O₂ extraction in peripheral capillaries due to slowed blood flow", td), Paragraph("Inadequate oxygenation of blood in the lungs (low SaO₂)", td)],
[Paragraph("<b>Causes</b>", tb), Paragraph("Cold exposure, reduced cardiac output, peripheral vascular disease, shock, Raynaud's", td), Paragraph("Lung disease (severe pneumonia, ARDS, PE), congenital heart disease with R→L shunt", td)],
[Paragraph("<b>Sites to examine</b>", tb), Paragraph("Nail beds, fingertips, tip of nose, palms, toes", td), Paragraph("Tongue, lips, buccal mucosa — tongue is KEY", td)],
[Paragraph("<b>Tongue</b>", tb), Paragraph("NOT affected", td), Paragraph("YES — cyanosed tongue = central cyanosis (diagnostic)", td)],
[Paragraph("<b>SaO₂</b>", tb), Paragraph("Normal or near-normal arterial O₂", td), Paragraph("Reduced (<94%)", td)],
]
tc = Table(cyan_data, colWidths=[3.2*cm, (BODY_W-3.2*cm)/2, (BODY_W-3.2*cm)/2])
tc.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), TABLE_HEADER),
("BACKGROUND", (0,1),(0,-1), LIGHT_BLUE),
("ROWBACKGROUNDS",(1,1),(-1,-1),[TABLE_EVEN, TABLE_ALT]),
("ALIGN", (0,0),(-1,-1),"LEFT"),
("VALIGN", (0,0),(-1,-1),"TOP"),
("GRID", (0,0),(-1,-1),0.5, BORDER_GRAY),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
]))
story.append(tc)
story.append(SP(0.2))
story.append(P(
"<b>Special note:</b> Carbon monoxide poisoning → <b>cherry-red</b> discolouration (not cyanosis). "
"Methaemoglobinaemia (phenacetin, dapsone) → cyanosis with <b>normal arterial O₂ tension</b>."
))
story.append(SP(0.3))
story.append(H2("5.3 Jaundice"))
story.append(P(
"Jaundice (icterus) = yellow discolouration of skin and mucous membranes from excess "
"<b>bilirubin</b> in plasma. Clinically detectable when serum bilirubin exceeds "
"<b>2–3 mg/dL</b> (normal <1 mg/dL). Bilirubin is a lipid-soluble pigment derived from haem breakdown."
))
story.append(SP(0.1))
story.append(P("<b>Where to look for jaundice:</b>"))
for s in [
"Sclera of the eyeball — earliest and most reliable site.",
"Skin (especially fair-skinned individuals).",
"Mucous membranes — hard palate, underside of tongue.",
"Urine — dark (conjugated bilirubinuria in obstructive/hepatic jaundice).",
"Stool — pale/clay-coloured in obstructive jaundice.",
]:
story.append(B(s))
story.append(SP(0.2))
story.append(mktable(
["Type", "Serum Bilirubin", "Colour", "Urine", "Stool", "Causes"],
[
["Pre-hepatic (Haemolytic)", "Unconjugated ↑", "Lemon-yellow", "Normal / dark urobilinogen", "Normal/dark", "Haemolytic anaemia, sickle cell, malaria, G6PD deficiency"],
["Hepatic (Hepatocellular)", "Both fractions ↑", "Yellow-orange", "Dark (bilirubin + urobilinogen)", "Pale", "Viral hepatitis A/B/C, alcoholic liver disease, cirrhosis, drugs"],
["Post-hepatic (Obstructive)", "Conjugated ↑↑", "Deep greenish-yellow (olive)", "Dark (bilirubinuria; no urobilinogen)", "Pale / clay", "Gallstones (CBD), carcinoma head of pancreas, cholangiocarcinoma, stricture"],
],
col_widths=[3.0*cm, 2.5*cm, 2.2*cm, 2.4*cm, 1.8*cm, BODY_W - 11.9*cm]
))
story.append(PageBreak())
# ============================================================
# 6. VITAL SIGNS
# ============================================================
story.append(sec("6. VITAL SIGNS"))
story.append(SP(0.3))
story.append(P(
"Vital signs are the cornerstone of general examination and must be recorded at the start "
"of every clinical encounter. They are essential for emergency triage (ABCDE approach). "
"An abnormal vital sign is often the earliest sign of deterioration."
))
story.append(SP(0.2))
story.append(H2("6.1 Pulse"))
story.append(P(
"Palpate the <b>radial pulse</b> (lateral to flexor carpi radialis tendon) with index and middle fingers. "
"Assess the following parameters systematically:"
))
story.append(mktable(
["Parameter", "Normal / Description", "Abnormalities and Significance"],
[
["Rate", "60–100 bpm (adult)", "Bradycardia <60: complete heart block, hypothyroidism, athletes, beta-blockers.\nTachycardia >100: fever, anaemia, thyrotoxicosis, heart failure, arrhythmia, pain."],
["Rhythm", "Regular", "Regularly irregular: 2nd degree AV block, ectopics.\nIrregularly irregular: atrial fibrillation (characteristic)."],
["Volume / Amplitude", "Normal", "Bounding: fever, aortic regurgitation, thyrotoxicosis, AV fistula.\nThready/weak: shock, hypovolaemia, severe cardiac failure."],
["Character", "Normal upstroke and downstroke", "Slow rising/plateau: aortic stenosis.\nCollapsing/water-hammer: aortic regurgitation.\nPulsus paradoxus (↓vol with inspiration): cardiac tamponade, severe asthma."],
["Radio-femoral delay", "Simultaneous", "Delay = coarctation of aorta (classic sign)."],
["Radio-radial delay", "Simultaneous", "Delay = subclavian artery stenosis or coarctation."],
],
col_widths=[3.2*cm, 3.5*cm, BODY_W - 6.7*cm]
))
story.append(SP(0.3))
story.append(H2("6.2 Blood Pressure"))
story.append(P(
"Measure in both arms initially. Normal: <b><120/80 mmHg</b>. "
"Use a correctly sized cuff. Record systolic (Korotkoff I) and diastolic (Korotkoff V)."
))
story.append(mktable(
["BP Category", "Values", "Clinical Action"],
[
["Normal", "<120/80 mmHg", "No intervention needed; reassess periodically."],
["Elevated (pre-hypertension)", "120–129 / <80 mmHg", "Lifestyle modification."],
["Hypertension Grade 1", "130–139 / 80–89 mmHg", "Lifestyle changes; medication if high CV risk."],
["Hypertension Grade 2", "≥140 / ≥90 mmHg", "Antihypertensive therapy + lifestyle changes."],
["Hypertensive Urgency", "≥180/120 mmHg, no end-organ damage", "Oral antihypertensives; close monitoring."],
["Hypertensive Emergency", "≥180/120 mmHg + end-organ damage", "IV antihypertensives; ICU; immediate senior review."],
["Hypotension", "<90/60 mmHg", "Dehydration, shock, adrenal insufficiency, drugs."],
["Postural hypotension", "Drop ≥20 mmHg systolic on standing", "Dehydration, autonomic neuropathy, elderly, antihypertensives."],
],
col_widths=[4.5*cm, 4.5*cm, BODY_W - 9.0*cm]
))
story.append(SP(0.3))
story.append(H2("6.3 Temperature"))
story.append(mktable(
["Category", "Temperature Range", "Causes / Significance"],
[
["Normal", "36.5–37.5°C (oral)", "Rectal temp 0.3–0.5°C higher; axillary 0.5°C lower."],
["Low-grade fever", "37.3–38.0°C", "Early/viral infection, malignancy, connective tissue disease."],
["Moderate fever", "38.0–39.0°C", "Bacterial infection, systemic inflammatory disease, lymphoma."],
["High fever", "39.0–40.0°C", "Severe bacterial infection, falciparum malaria, drug fever."],
["Hyperpyrexia", ">40.0°C", "Heat stroke, CNS infection, malignant hyperthermia (anaesthetic emergency)."],
["Hypothermia", "<35.0°C", "Cold exposure, myxoedema coma, hypoadrenalism, severe septic shock."],
],
col_widths=[3.5*cm, 3.5*cm, BODY_W - 7.0*cm]
))
story.append(SP(0.3))
story.append(H2("6.4 Respiratory Rate"))
story.append(P(
"Count for a full <b>60 seconds</b> while the patient is unaware (often counted while appearing to "
"take the pulse). Normal adult: <b>12–20 breaths/min</b>. Often the earliest vital sign to change "
"in deterioration yet the most frequently underdocumented."
))
for item in [
"Tachypnoea (>20/min): pneumonia, PE, heart failure, metabolic acidosis, anxiety, sepsis, pain.",
"Bradypnoea (<12/min): opioid overdose, CNS depression, raised intracranial pressure.",
"Cheyne-Stokes respiration: cyclical crescendo-decrescendo — cardiac failure, uraemia, brain injury.",
"Kussmaul breathing: deep, sighing, rapid — diabetic ketoacidosis (compensating metabolic acidosis).",
"Biot's (ataxic) breathing: irregular clusters with apnoeas — brainstem (medulla) injury.",
]:
story.append(B(item))
story.append(PageBreak())
# ============================================================
# 7. PERIPHERAL SIGNS
# ============================================================
story.append(sec("7. PERIPHERAL SIGNS — HANDS, NAILS, EYES, LYMPH NODES"))
story.append(SP(0.3))
story.append(H2("7.1 Hands and Nails"))
story.append(mktable(
["Sign", "Appearance", "Clinical Association"],
[
["Clubbing", "Obliteration of nail-fold angle (>180°); 'drumstick' fingers; Schamroth's sign positive", "Lung carcinoma, bronchiectasis, empyema, cyanotic CHD, cirrhosis, IBD, infective endocarditis"],
["Koilonychia (spoon nails)", "Concave, spoon-shaped nails", "Iron deficiency anaemia (chronic)"],
["Leukonychia", "White nails (Terry's — white with distal pink band)", "Hypoalbuminaemia, cirrhosis, nephrotic syndrome, cardiac failure"],
["Onycholysis", "Separation of nail plate from bed (distal)", "Thyrotoxicosis, psoriasis, fungal infection, trauma"],
["Beau's lines", "Transverse grooves across all nails", "Previous serious systemic illness, chemotherapy, severe infection"],
["Splinter haemorrhages", "Linear dark-red streaks longitudinally under nail", "Infective endocarditis (classic), vasculitis, trauma"],
["Peripheral cyanosis", "Bluish nail bed", "Reduced peripheral perfusion, shock, cold exposure"],
["Palmar erythema", "Redness of thenar and hypothenar eminences", "Liver cirrhosis, pregnancy, thyrotoxicosis, RA"],
["Dupuytren's contracture", "Fibrotic thickening of palmar fascia, ring finger flexion", "Cirrhosis (alcoholic), idiopathic, trauma, epilepsy (phenytoin)"],
["Asterixis (flapping tremor)", "Coarse flapping tremor on outstretched hands", "Hepatic encephalopathy, CO₂ retention, uraemia"],
],
col_widths=[3.8*cm, 4.5*cm, BODY_W - 8.3*cm]
))
story.append(SP(0.3))
story.append(H2("7.2 Eyes"))
story.append(mktable(
["Sign", "Appearance / Finding", "Clinical Significance"],
[
["Icterus (jaundice)", "Yellow sclera", "Hyperbilirubinaemia — earliest and most sensitive site"],
["Conjunctival pallor", "Pale lower palpebral conjunctiva", "Anaemia"],
["Exophthalmos / Proptosis", "Protrusion of eyeball beyond orbital rim", "Thyrotoxicosis (Graves'), orbital mass, cavernous sinus fistula"],
["Lid lag", "Upper lid lags behind eyeball on downward gaze", "Thyrotoxicosis"],
["Periorbital oedema", "Puffiness around the eyes, especially morning", "Myxoedema, nephrotic syndrome, angioedema, allergy"],
["Kayser-Fleischer rings", "Golden-brown ring at the periphery of cornea", "Wilson's disease (hepatolenticular degeneration) — pathognomonic"],
["Corneal arcus", "Grey-white ring around cornea periphery", "Hypercholesterolaemia if age <45; normal senile change if >65"],
["Xanthelasma", "Yellow lipid plaques on upper/lower eyelids", "Hyperlipidaemia (type IIa/IIb), may be familial"],
["Argyll Robertson pupil", "Accommodates but does NOT react to light", "Neurosyphilis (tabes dorsalis, GPI)"],
["Horner's syndrome", "Ptosis, miosis, anhidrosis, enophthalmos", "Pancoast tumour, carotid artery dissection, brainstem lesion"],
["Papilloedema", "Blurring of optic disc margin on fundoscopy", "Raised intracranial pressure"],
],
col_widths=[4.0*cm, 5.0*cm, BODY_W - 9.0*cm]
))
story.append(SP(0.3))
story.append(H2("7.3 Lymphadenopathy"))
story.append(P(
"In any malignant swelling, examine all regional and distant lymph node groups. "
"In lymph node enlargement, always identify the likely drainage territory to determine "
"the cause. Note: size, consistency, tenderness, fixity, overlying skin changes."
))
story.append(mktable(
["Lymph Node Group", "Clinical Significance"],
[
["Cervical (anterior triangle)", "Dental infection, oral/pharyngeal malignancy, lymphoma"],
["Cervical (posterior triangle)", "EBV (infectious mononucleosis), rubella, TB, lymphoma"],
["Left supraclavicular — Virchow's node (Troisier's sign)", "Intra-abdominal malignancy (stomach, colon, pancreas) — must always be examined"],
["Axillary", "Breast carcinoma, upper limb infection/melanoma, lymphoma"],
["Inguinal", "Lower limb infection, STIs, pelvic/abdominal malignancy, melanoma of lower limb"],
["Mediastinal (on CXR/CT)", "Lymphoma, sarcoidosis, TB, lung carcinoma"],
["Generalised lymphadenopathy", "EBV, HIV, lymphoma, CLL, sarcoidosis, SLE, TB"],
],
col_widths=[6.0*cm, BODY_W - 6.0*cm]
))
story.append(PageBreak())
# ============================================================
# 8. SYSTEMIC EXAMINATION CHECKLIST
# ============================================================
story.append(sec("8. SYSTEMIC EXAMINATION CHECKLIST"))
story.append(SP(0.3))
story.append(H2("8.1 Head and Neck"))
for item in [
"Skull: shape, asymmetry, tenderness, palpable swellings (erosion of skull in dermoid cyst).",
"Eyes: jaundice, pallor, exophthalmos, lid lag, Horner's, corneal arcus, xanthelasma, fundoscopy.",
"Ears: discharge, deformity, mastoid tenderness (Battle's sign in basal skull fracture).",
"Nose: discharge, obstruction, epistaxis, septal deviation.",
"Mouth and Throat: dentition, gum disease, tonsils, tongue (size, colour, coating, fissures, fasciculation).",
"Neck — Trachea: central or deviated (deviates away from tension pneumothorax/large effusion; towards collapse/fibrosis).",
"Neck — Thyroid: size, consistency, nodularity, bruit (retrosternal extension, tracheal compression).",
"JVP (Jugular Venous Pressure): normal <4 cm above sternal angle. Assess waveform (a, c, v waves). Elevated in: RHF, cardiac tamponade, SVC obstruction, constrictive pericarditis.",
"Carotid arteries: palpate pulse, listen for bruit.",
"Lymph nodes: all cervical groups, supraclavicular.",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("8.2 Upper Limbs"))
for item in [
"General: wasting, deformities, scars, tremor, asterixis.",
"Vascular: radial pulse (both sides simultaneously — for radio-radial delay), capillary refill, Allen's test.",
"Neurological: power (MRC grading 0–5), tone, reflexes (biceps C5–6, triceps C7, supinator C5–6), sensation (all modalities).",
"Axillae: lymph nodes (anterior, posterior, lateral, medial, apical groups).",
"Joints: shoulder, elbow, wrist, MCP, PIP, DIP — ROM, deformity, swelling, tenderness, crepitus.",
"Nails: clubbing, koilonychia, leukonychia, splinter haemorrhages, onycholysis.",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("8.3 Thorax — Respiratory and Cardiovascular"))
story.append(H4("Respiratory Examination"))
story.append(mktable(
["Step", "What to Assess", "Key Findings"],
[
["Inspection", "Chest shape, symmetry, respiratory effort, use of accessory muscles, paradoxical movement", "Barrel chest (emphysema), pigeon chest (rickets), funnel (pectus excavatum), kyphoscoliosis, Harrison's sulcus"],
["Palpation", "Chest expansion (symmetry), tactile vocal fremitus, trachea position", "Reduced on affected side in effusion, collapse, pneumothorax; increased in consolidation"],
["Percussion", "Both sides comparatively, front and back", "Dull = consolidation/collapse; Stony dull = effusion; Hyper-resonant = pneumothorax/emphysema"],
["Auscultation", "Breath sounds, added sounds", "Vesicular (normal); Bronchial (consolidation); Crackles (pulmonary oedema, fibrosis); Wheeze (asthma/COPD); Pleural rub (pleuritis)"],
],
col_widths=[2.5*cm, 5.0*cm, BODY_W - 7.5*cm]
))
story.append(SP(0.2))
story.append(H4("Cardiovascular Examination"))
for item in [
"Inspection: chest wall pulsations, scars (median sternotomy, CABG, pacemaker pocket), dilated veins.",
"Apex beat: normally 5th ICS mid-clavicular line. Displaced (LV dilatation); heaving (pressure overload/LVH); tapping (MS — palpable S1).",
"Parasternal heave: right ventricular hypertrophy.",
"Thrills: palpable murmurs (grade ≥4).",
"Heart sounds S1, S2; extra sounds — S3 gallop (ventricular failure); S4 (hypertensive heart disease/LVH); opening snap (MS).",
"Murmurs: timing, location, radiation, character — systolic vs. diastolic; grading I–VI (Levine).",
"Peripheral oedema: pitting suggests CCF, nephrotic syndrome, liver failure.",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("8.4 Abdomen"))
story.append(P(
"The patient should lie flat with hips extended. Flex hips and knees for palpation to relax abdominal muscles. "
"Expose from xiphisternum to pubic symphysis. Inspect from the foot of the bed first."
))
story.append(mktable(
["Step", "What to Examine", "Key Findings"],
[
["Inspection", "Contour, distension, scars, stomas, dilated veins, umbilicus, visible peristalsis/pulsation, hernial orifices", "Caput medusae (portal HT); Grey Turner's/Cullen's signs; visible peristalsis (obstruction); pulsatile mass (AAA)"],
["Palpation (light)", "Tenderness, guarding, rigidity, abdominal wall", "Guarding = voluntary; Rigidity = involuntary (peritonitis); Rebound tenderness (peritoneal irritation)"],
["Palpation (deep)", "Organomegaly — liver, spleen, kidneys; masses", "Hepatomegaly (smooth = CCF/hepatitis; nodular = cirrhosis/metastases); Splenomegaly; Ballottable kidneys"],
["Percussion", "Liver span, splenic area, shifting dullness, suprapubic dullness", "Shifting dullness: ascites >500 mL; Fluid thrill: massive ascites"],
["Auscultation", "Bowel sounds, vascular bruits", "Absent BS: paralytic ileus; Tinkling BS: intestinal obstruction; Bruit: renal artery stenosis/AAA"],
["Special signs", "Murphy's sign, psoas sign, obturator sign, Rovsing's sign", "Murphy's: cholecystitis; Psoas/Obturator/Rovsing's: appendicitis"],
["Rectal examination", "Prostate, rectal wall, masses, stool, blood", "MUST NOT BE OMITTED — rectal carcinoma, prostatic enlargement/carcinoma, melaena"],
],
col_widths=[2.5*cm, 5.0*cm, BODY_W - 7.5*cm]
))
story.append(SP(0.3))
story.append(H2("8.5 Lower Limbs"))
for item in [
"Vascular: femoral, popliteal, dorsalis pedis, posterior tibial pulses; skin colour, temperature, hair distribution; ankle-brachial pressure index (ABPI) if PVD suspected.",
"Neurological: power (hip flexion/extension, knee, ankle), tone, reflexes (knee jerk L3–4, ankle jerk S1–2), plantar response (Babinski), sensation (all modalities), coordination (heel-shin test).",
"Varicose veins: distribution, Trendelenburg test (sapheno-femoral incompetence), tap test; skin changes (eczema, lipodermatosclerosis, venous ulcer over medial malleolus).",
"Oedema: pitting (cardiac, renal, hepatic, nutritional, venous) vs. non-pitting (lymphoedema, pretibial myxoedema in hypothyroidism).",
"Joints: hip, knee, ankle — range of motion, deformity, effusion, crepitus, tenderness.",
]:
story.append(B(item))
story.append(PageBreak())
# ============================================================
# 9. RED FLAGS
# ============================================================
story.append(sec("9. RED FLAG SIGNS"))
story.append(SP(0.3))
story.append(warn_box("Red Flags Requiring Urgent Investigation and/or Referral", [
"Unexplained weight loss >10% body weight in 6 months — malignancy, TB, HIV, endocrine disease.",
"Haemoptysis — pulmonary carcinoma, TB, bronchiectasis, pulmonary embolism.",
"Haematemesis or malaena — upper GI bleed (peptic ulcer, oesophageal varices, carcinoma).",
"Rectal bleeding — colorectal carcinoma, IBD, angiodysplasia.",
"Persistent dysphagia — oesophageal carcinoma, achalasia, extrinsic compression.",
"Unexplained dyspnoea at rest — acute cardiac failure, massive PE, pneumonia, pleural effusion.",
"New focal neurological deficit — stroke, cord compression, intracranial mass, meningitis.",
"Sudden severe 'thunderclap' headache — subarachnoid haemorrhage until proven otherwise.",
"Severe abdominal pain with board-like rigidity — peritonitis, perforated viscus, leaking AAA.",
"High fever >39°C with altered consciousness — septic encephalopathy, meningitis.",
"Progressive bone pain + hypercalcaemia — multiple myeloma, bony metastases.",
"Virchow's (left supraclavicular) lymph node enlargement — intra-abdominal malignancy.",
"Bowel or bladder dysfunction with back pain — cauda equina syndrome (surgical emergency).",
"Pulsatile abdominal mass + hypotension — ruptured abdominal aortic aneurysm.",
]))
story.append(SP(0.3))
story.append(info_box("Red Flags in Spinal Pain (Goldman-Cecil Medicine)", [
"History of cancer or unexplained weight loss — vertebral metastasis.",
"Fever, IV drug use, or recent systemic infection — vertebral osteomyelitis / discitis.",
"Recent significant trauma — fracture (especially osteoporotic).",
"Bowel/bladder dysfunction + back pain — cauda equina syndrome (emergency decompression).",
"Progressive motor or sensory deficit — spinal cord compression.",
"Night pain / rest pain not relieved by position changes — malignancy or infection.",
"Prolonged corticosteroid use — risk of osteoporotic vertebral fracture.",
], color=WARN_ORANGE, bg=WARN_BG, bullet_char="!"))
story.append(PageBreak())
# ============================================================
# 10. INVESTIGATIONS
# ============================================================
story.append(sec("10. SPECIAL INVESTIGATIONS"))
story.append(SP(0.3))
story.append(P(
"After clinical examination, formulate a <b>provisional diagnosis</b> and <b>differential diagnoses</b>. "
"Then select investigations to confirm or refute these. Remember: <i>'If you diagnose a rare disease, "
"you will be rarely correct'</i> — common diagnoses first."
))
story.append(SP(0.2))
story.append(mktable(
["Investigation", "What It Assesses", "When to Order"],
[
["Full Blood Count (FBC)", "WBC (infection/leukaemia), Hb (anaemia), platelets", "All patients; leucocytosis in infection; ↓Hb + ↑ESR in malignancy"],
["ESR / CRP", "Non-specific markers of inflammation", "Chronic swellings, malignancy, connective tissue disease"],
["Blood Glucose (fasting)", "Diabetes mellitus", "Recurrent abscesses/carbuncles, poor wound healing — always check"],
["Urea, Creatinine, Electrolytes", "Renal function and fluid-electrolyte balance", "Oedema, hypertension, altered urine output, pre-operative"],
["Liver Function Tests (LFTs)", "Bilirubin, AST, ALT, ALP, GGT, albumin, PT", "Jaundice, hepatomegaly, alcohol history, malignancy, drugs"],
["Serology", "VDRL/RPR (syphilis), HIV, Hepatitis B & C", "Suspected STI, condyloma, gumma, atypical swelling"],
["Urine Analysis", "Glucose, protein, blood, cells, casts, culture", "Recurrent infection (glucose), renal disease (protein/blood/casts)"],
["Fine Needle Aspiration Biopsy (FNAB)", "Cytological diagnosis from solid or cystic swelling", "Thyroid nodule, lymph node, breast lump — safest initial biopsy"],
["Aspiration + fluid analysis", "Physical, chemical, microscopic, bacteriological", "Cystic swellings, joint effusions, pleural/ascitic fluid"],
["Imaging: X-ray, USS, CT, MRI", "Morphology, extent, vascularity, invasion, metastasis", "Directed by clinical findings and provisional diagnosis"],
["Biopsy (incisional/excisional)", "Histopathological diagnosis", "When FNAB inconclusive; essential before definitive cancer surgery"],
],
col_widths=[3.8*cm, 4.8*cm, BODY_W - 8.6*cm]
))
story.append(PageBreak())
# ============================================================
# 11. DIAGNOSIS & TREATMENT
# ============================================================
story.append(sec("11. CLINICAL DIAGNOSIS AND TREATMENT PLAN"))
story.append(SP(0.3))
story.append(P(
"A complete <b>clinical diagnosis</b> identifies: the affected organ, the exact site, the nature of the "
"pathological process, the extent and stage, structures involved in spread or metastasis, and complications."
))
story.append(SP(0.1))
for item in [
"Affected organ and precise site (e.g., carcinoma of <i>right upper outer quadrant</i> of breast).",
"Nature of the pathological process: inflammatory, neoplastic, degenerative, congenital, traumatic, vascular.",
"Extent and clinical staging (e.g., T2N1M0 breast carcinoma; Stage IIIB lung carcinoma).",
"Structures involved in spread: regional lymph nodes, adjacent organs, distant metastases.",
"For hernia: direct or indirect; reducible/irreducible/obstructed/strangulated; contents.",
"Complications present: obstruction, strangulation, perforation, haemorrhage, infection.",
]:
story.append(B(item))
story.append(SP(0.3))
story.append(H2("Treatment Planning"))
story.append(mktable(
["Phase", "Key Actions"],
[
["Medical Treatment", "Drug therapy (dosing, duration, monitoring), dietary modification, physiotherapy, conservative management, observation."],
["Surgical Treatment", "Type of procedure, surgical approach (open/laparoscopic/endoscopic/robotic), anaesthetic type, timing (elective/urgent/emergency), intended outcome."],
["Pre-operative Preparation", "Optimise co-morbidities (DM, CCF, COPD); correct anaemia; stop anticoagulants as per protocol; IV access; NBM instructions; informed consent; DVT prophylaxis; antibiotic prophylaxis."],
["Intra-operative Phase", "Anaesthesia; procedure execution; document intraoperative findings; haemostasis; specimen for histology."],
["Post-operative Period", "Vital sign monitoring; analgesia; IV fluid management; wound care; drain management; early mobilisation; VTE prophylaxis; physiotherapy."],
["Follow-Up", "Outpatient review; histopathology results; adjuvant chemotherapy/radiotherapy if indicated; surveillance imaging; patient education."],
],
col_widths=[4.2*cm, BODY_W - 4.2*cm]
))
story.append(PageBreak())
# ============================================================
# 12. SUMMARY TABLE
# ============================================================
story.append(sec("12. SUMMARY TABLE — KEY CLINICAL SIGNS AT A GLANCE"))
story.append(SP(0.3))
story.append(mktable(
["Clinical Sign", "Where to Look / How to Elicit", "Associated Condition"],
[
["Pallor", "Lower palpebral conjunctiva, nail beds, palmar creases", "Anaemia, haemorrhage, shock"],
["Central cyanosis", "Tongue, lips, buccal mucosa", "Lung disease, R→L cardiac shunt, severe cardiac failure"],
["Peripheral cyanosis", "Nail beds, fingertips, toes, tip of nose", "Reduced peripheral flow, cold, shock"],
["Jaundice", "Sclera (earliest), skin, mucous membranes", "Pre-hepatic, hepatic, post-hepatic jaundice"],
["Clubbing", "Nail-fold angle obliteration, Schamroth sign", "Lung Ca, bronchiectasis, cyanotic CHD, cirrhosis, IE"],
["Koilonychia", "Spoon-shaped concave nails", "Iron deficiency anaemia"],
["Splinter haemorrhages", "Linear dark streaks beneath nail", "Infective endocarditis, vasculitis, trauma"],
["Leukonychia", "White nails (Terry's nails)", "Hypoalbuminaemia, cirrhosis, nephrotic syndrome"],
["Asterixis", "Flapping tremor on outstretched hands", "Hepatic encephalopathy, CO₂ retention, uraemia"],
["Palmar erythema", "Redness of thenar/hypothenar eminences", "Cirrhosis, pregnancy, thyrotoxicosis, RA"],
["Xanthelasma", "Yellow lipid plaques on eyelids", "Hyperlipidaemia"],
["Kayser-Fleischer ring", "Golden-brown corneal ring (slit-lamp)", "Wilson's disease — pathognomonic"],
["Horner's syndrome", "Ptosis, miosis, anhidrosis", "Pancoast tumour, carotid dissection, brainstem lesion"],
["Virchow's node", "Left supraclavicular lymph node", "Intra-abdominal malignancy (Troisier's sign)"],
["Cullen's sign", "Periumbilical bruising", "Severe acute pancreatitis, ruptured ectopic pregnancy"],
["Grey Turner's sign", "Flank bruising", "Retroperitoneal haemorrhage, severe pancreatitis"],
["Murphy's sign", "Pain arrest on deep RUQ palpation during inspiration", "Acute cholecystitis"],
["Shifting dullness", "Dullness shifts with change in position", "Ascites (>500 mL)"],
["JVP elevation", "Distended neck veins (internal jugular)", "RHF, SVC obstruction, cardiac tamponade"],
["Pulsus paradoxus", "Pulse volume decreases on inspiration", "Cardiac tamponade, severe asthma"],
["Caput medusae", "Dilated periumbilical veins radiating outward", "Portal hypertension (liver cirrhosis)"],
["Gynaecomastia", "Enlarged male breast tissue", "Cirrhosis, drugs (spironolactone, digoxin), hypogonadism"],
["Facies hippocratica", "Sunken eyes, hollow cheeks, pale, cold skin", "Generalised peritonitis, terminal illness"],
["Risus sardonicus", "Fixed grin (facial muscle spasm)", "Tetanus"],
["Pretibial myxoedema", "Non-pitting oedema over shins, waxy skin", "Hypothyroidism (Graves' disease)"],
["Battle's sign", "Bruising behind ear (mastoid)", "Basal skull fracture"],
["Racoon eyes", "Periorbital bruising", "Basal skull fracture (anterior fossa)"],
],
col_widths=[4.3*cm, 5.0*cm, BODY_W - 9.3*cm]
))
story.append(SP(0.4))
story.append(HR())
story.append(P(
"<b>Sources:</b> S Das — A Manual on Clinical Surgery, 13th Edition | "
"Bailey and Love's Short Practice of Surgery, 28th Edition | "
"Goldman-Cecil Medicine, International Edition | "
"Bradley and Daroff's Neurology in Clinical Practice."
))
# ============================================================
doc.build(story)
print("PDF generated successfully:", OUTPUT_PATH)
What happened ?