Explain this
| Parameter | Finding |
|---|---|
| Projection | PA (patient faces the film; X-ray beam enters from behind) |
| Rotation | None - clavicle heads symmetric around the spine |
| Inspiration | Adequate (~9 posterior ribs visible) |
| Penetration | Adequate - spine faintly visible through cardiac shadow |
| Diagnosis | Supporting Feature |
|---|---|
| Sarcoidosis | Perihilar distribution + bilateral hilar prominence is classic (BHL - bilateral hilar lymphadenopathy) |
| Atypical/Viral Pneumonia | Bilateral diffuse interstitial pattern (Mycoplasma, COVID-19, influenza) |
| Miliary Tuberculosis | Fine uniform nodules throughout; consider in endemic areas |
| Pneumoconiosis | Occupational exposure history; upper/mid-zone nodular pattern |
| Interstitial Lung Disease (ILD) | Chronic course; check for fibrotic changes on HRCT |
Create a quick reference PDF on chest X-ray interpretation
mkdir -p /tmp/workspace/cxr-reference && ls /tmp/workspace/cxr-reference/
~/cxr-reference/make_cxr_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import PageBreak
OUTPUT = "/tmp/workspace/cxr-reference/CXR_Quick_Reference.pdf"
# ── Colours ──────────────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1A3A5C")
MID_BLUE = colors.HexColor("#2E6DA4")
LIGHT_BLUE = colors.HexColor("#D6E8F7")
TEAL = colors.HexColor("#1B7A6E")
LIGHT_TEAL = colors.HexColor("#D0EFEB")
ORANGE = colors.HexColor("#D4500A")
LIGHT_ORANGE= colors.HexColor("#FDE9DC")
YELLOW_BG = colors.HexColor("#FFF8DC")
WHITE = colors.white
GREY_LINE = colors.HexColor("#CCCCCC")
DARK_TEXT = colors.HexColor("#1A1A1A")
LIGHT_GREY = colors.HexColor("#F5F5F5")
RED_ERR = colors.HexColor("#C0392B")
GREEN_OK = colors.HexColor("#1E8449")
# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
TITLE = S("Title2", fontName="Helvetica-Bold", fontSize=22, textColor=WHITE, alignment=TA_CENTER, spaceAfter=4)
SUBTITLE= S("Sub", fontName="Helvetica", fontSize=11, textColor=LIGHT_BLUE, alignment=TA_CENTER, spaceAfter=2)
H1 = S("H1", fontName="Helvetica-Bold", fontSize=13, textColor=WHITE, alignment=TA_LEFT, spaceAfter=2, spaceBefore=4)
H2 = S("H2", fontName="Helvetica-Bold", fontSize=11, textColor=DARK_BLUE, alignment=TA_LEFT, spaceAfter=3, spaceBefore=6)
H3 = S("H3", fontName="Helvetica-BoldOblique", fontSize=10, textColor=TEAL, alignment=TA_LEFT, spaceAfter=2, spaceBefore=4)
BODY = S("Body2", fontName="Helvetica", fontSize=9, textColor=DARK_TEXT, alignment=TA_JUSTIFY, spaceAfter=2, leading=13)
BULLET = S("Bullet2", fontName="Helvetica", fontSize=9, textColor=DARK_TEXT, alignment=TA_LEFT, spaceAfter=2, leftIndent=12, leading=13)
SMALL = S("Small", fontName="Helvetica", fontSize=8, textColor=colors.grey, alignment=TA_CENTER)
TABLE_H = S("TblH", fontName="Helvetica-Bold", fontSize=9, textColor=WHITE, alignment=TA_CENTER)
TABLE_B = S("TblB", fontName="Helvetica", fontSize=8.5,textColor=DARK_TEXT, alignment=TA_LEFT, leading=12)
TABLE_BC= S("TblBC", fontName="Helvetica", fontSize=8.5,textColor=DARK_TEXT, alignment=TA_CENTER,leading=12)
WARN = S("Warn", fontName="Helvetica-Bold", fontSize=9, textColor=RED_ERR, alignment=TA_LEFT, spaceAfter=2)
NOTE = S("Note", fontName="Helvetica-Oblique",fontSize=8.5,textColor=DARK_BLUE,alignment=TA_LEFT, spaceAfter=2, leading=12)
def section_header(text, colour=DARK_BLUE):
"""Full-width coloured section header bar."""
data = [[Paragraph(text, H1)]]
t = Table(data, colWidths=[175*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colour),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS",(0,0), (-1,-1), [colour]),
]))
return t
def box_table(data_rows, col_widths, header_colour=MID_BLUE, stripe=True):
"""Generic styled table."""
t = Table(data_rows, colWidths=col_widths, repeatRows=1)
style_cmds = [
("BACKGROUND", (0,0), (-1,0), header_colour),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 9),
("ALIGN", (0,0), (-1,0), "CENTER"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, GREY_LINE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]
if stripe:
for r in range(1, len(data_rows)):
bg = LIGHT_GREY if r % 2 == 1 else WHITE
style_cmds.append(("BACKGROUND", (0,r), (-1,r), bg))
t.setStyle(TableStyle(style_cmds))
return t
# ── Document ──────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT, pagesize=A4,
leftMargin=18*mm, rightMargin=18*mm,
topMargin=15*mm, bottomMargin=15*mm,
title="Chest X-Ray Quick Reference",
author="Orris Medical Reference"
)
story = []
W = 174*mm # usable width
# ════════════════════════════════════════════════════════════════════════════
# COVER BANNER
# ════════════════════════════════════════════════════════════════════════════
cover_data = [[
Paragraph("CHEST X-RAY", TITLE),
Paragraph("Quick Reference Guide", SUBTITLE),
Paragraph("Systematic Interpretation · Patterns · Differentials · Key Measurements", SMALL),
]]
cover_t = Table([[
Paragraph("CHEST X-RAY", TITLE),
]], colWidths=[W])
cover_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
("TOPPADDING", (0,0), (-1,-1), 14),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 10),
]))
story.append(cover_t)
sub_t = Table([[Paragraph("Quick Reference Guide for Clinical Practice", SUBTITLE)]], colWidths=[W])
sub_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 14),
("LEFTPADDING", (0,0), (-1,-1), 10),
]))
story.append(sub_t)
story.append(Spacer(1, 6*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1 — TECHNICAL ADEQUACY (RIPE)
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. TECHNICAL ADEQUACY — The RIPE Checklist"))
story.append(Spacer(1, 3*mm))
ripe_data = [
[Paragraph("Letter", TABLE_H), Paragraph("Parameter", TABLE_H),
Paragraph("How to Assess", TABLE_H), Paragraph("Adequate When…", TABLE_H)],
[Paragraph("<b>R</b>", TABLE_BC), Paragraph("Rotation", TABLE_B),
Paragraph("Medial clavicle ends equidistant from spinous processes", TABLE_B),
Paragraph("Symmetric clavicle-spine gaps", TABLE_B)],
[Paragraph("<b>I</b>", TABLE_BC), Paragraph("Inspiration", TABLE_B),
Paragraph("Count posterior ribs above right hemidiaphragm", TABLE_B),
Paragraph("≥ 6 anterior / ≥ 9–10 posterior ribs visible", TABLE_B)],
[Paragraph("<b>P</b>", TABLE_BC), Paragraph("Projection", TABLE_B),
Paragraph("PA: scapulae lateral to lung fields; AP: scapulae overlap lungs", TABLE_B),
Paragraph("PA preferred — heart appears smaller than AP", TABLE_B)],
[Paragraph("<b>E</b>", TABLE_BC), Paragraph("Exposure / Penetration", TABLE_B),
Paragraph("Look for vertebrae through cardiac shadow", TABLE_B),
Paragraph("Vertebrae just visible; vessels visible in lungs", TABLE_B)],
]
story.append(box_table(ripe_data, [10*mm, 28*mm, 70*mm, 62*mm]))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2 — SYSTEMATIC APPROACH (ABCDE)
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("2. SYSTEMATIC APPROACH — ABCDE"))
story.append(Spacer(1, 3*mm))
abcde_data = [
[Paragraph("Letter", TABLE_H), Paragraph("Stands For", TABLE_H),
Paragraph("What to Look For", TABLE_H)],
[Paragraph("<b>A</b>", TABLE_BC), Paragraph("Airway", TABLE_B),
Paragraph("Trachea midline? Any deviation? Carina angle ≤ 70°. Check for foreign bodies, subglottic narrowing.", TABLE_B)],
[Paragraph("<b>B</b>", TABLE_BC), Paragraph("Breathing (Lungs)", TABLE_B),
Paragraph("Compare both lung fields: opacities, consolidation, collapse, pneumothorax, interstitial markings, nodules, masses.", TABLE_B)],
[Paragraph("<b>C</b>", TABLE_BC), Paragraph("Cardiac / Mediastinum", TABLE_B),
Paragraph("Cardiothoracic ratio < 0.5 on PA. Check cardiac borders (silhouette sign), mediastinal width < 8 cm, aortic knob.", TABLE_B)],
[Paragraph("<b>D</b>", TABLE_BC), Paragraph("Diaphragm / Below", TABLE_B),
Paragraph("Right hemidiaphragm higher than left (by 1.5–2.5 cm). Sharp costophrenic angles. Free air under diaphragm?", TABLE_B)],
[Paragraph("<b>E</b>", TABLE_BC), Paragraph("Everything Else", TABLE_B),
Paragraph("Bones (ribs, clavicles, spine): fractures, lytic/blastic lesions. Soft tissues: emphysema, foreign bodies, lines/tubes.", TABLE_B)],
]
story.append(box_table(abcde_data, [10*mm, 35*mm, 125*mm]))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3 — KEY MEASUREMENTS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("3. KEY MEASUREMENTS & NORMAL VALUES", TEAL))
story.append(Spacer(1, 3*mm))
meas_data = [
[Paragraph("Measurement", TABLE_H), Paragraph("Normal Value", TABLE_H),
Paragraph("Significance if Abnormal", TABLE_H)],
[Paragraph("Cardiothoracic (CT) ratio", TABLE_B),
Paragraph("< 0.5 on PA view", TABLE_B),
Paragraph("≥ 0.5 = cardiomegaly (CCF, pericardial effusion, dilated cardiomyopathy)", TABLE_B)],
[Paragraph("Mediastinal width", TABLE_B),
Paragraph("< 8 cm at aortic arch level", TABLE_B),
Paragraph("> 8 cm = consider aortic dissection, lymphoma, thymoma", TABLE_B)],
[Paragraph("Carina angle", TABLE_B),
Paragraph("≤ 70°", TABLE_B),
Paragraph("> 70° = left atrial enlargement (widened carina / 'splayed' carina)", TABLE_B)],
[Paragraph("Tracheal position", TABLE_B),
Paragraph("Midline, slight right deviation at aortic arch is normal", TABLE_B),
Paragraph("Deviation away from opacity = tension pneumothorax or large effusion; toward = collapse", TABLE_B)],
[Paragraph("Diaphragm height", TABLE_B),
Paragraph("Right 1.5–2.5 cm higher than left", TABLE_B),
Paragraph("Elevated = paralysis, subphrenic abscess, hepatomegaly, collapse; Flattened = hyperinflation (COPD)", TABLE_B)],
[Paragraph("Costophrenic angles", TABLE_B),
Paragraph("Acute, sharp", TABLE_B),
Paragraph("Blunting = ≥ 200–300 mL pleural fluid; obliteration = larger effusion", TABLE_B)],
[Paragraph("Hilum position", TABLE_B),
Paragraph("Left hilum 0.5–1.5 cm higher than right", TABLE_B),
Paragraph("Bilateral enlargement (BHL): sarcoidosis, lymphoma, TB; Unilateral: malignancy, infection", TABLE_B)],
]
story.append(box_table(meas_data, [48*mm, 42*mm, 80*mm], header_colour=TEAL))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4 — LUNG OPACITY PATTERNS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("4. LUNG OPACITY PATTERNS & DIFFERENTIALS"))
story.append(Spacer(1, 3*mm))
pattern_data = [
[Paragraph("Pattern", TABLE_H), Paragraph("Appearance on CXR", TABLE_H),
Paragraph("Key Differentials", TABLE_H), Paragraph("Clue", TABLE_H)],
[Paragraph("Consolidation", TABLE_B),
Paragraph("Homogeneous opacity, air bronchogram, no volume loss", TABLE_B),
Paragraph("Pneumonia, pulmonary oedema, infarction, haemorrhage, aspiration", TABLE_B),
Paragraph("Air bronchogram = airspace disease", TABLE_B)],
[Paragraph("Collapse / Atelectasis", TABLE_B),
Paragraph("Opacity with volume loss: shifted fissures, trachea/mediastinum pulled toward lesion", TABLE_B),
Paragraph("Mucus plug, endobronchial tumour, foreign body", TABLE_B),
Paragraph("Volume loss is key distinguishing feature", TABLE_B)],
[Paragraph("Pleural Effusion", TABLE_B),
Paragraph("Homogeneous opacity with meniscus; blunted costophrenic angle; opacifies base", TABLE_B),
Paragraph("CCF, malignancy, infection (empyema), TB, PE, trauma, hypoalbuminaemia", TABLE_B),
Paragraph("Blunts CP angle; > 500 mL shifts mediastinum away", TABLE_B)],
[Paragraph("Pneumothorax", TABLE_B),
Paragraph("Visible lung edge with absent lung markings beyond it", TABLE_B),
Paragraph("Spontaneous (tall thin male), trauma, iatrogenic, COPD, asthma", TABLE_B),
Paragraph("Check for tension: tracheal deviation, collapsed lung", TABLE_B)],
[Paragraph("Interstitial Pattern", TABLE_B),
Paragraph("Reticular, nodular, or reticulonodular; Kerley B lines (septal lines)", TABLE_B),
Paragraph("Pulmonary oedema (Kerley B), ILD, sarcoidosis, viral/atypical pneumonia, lymphangitis", TABLE_B),
Paragraph("Kerley B lines at periphery → pulmonary oedema", TABLE_B)],
[Paragraph("Miliary Pattern", TABLE_B),
Paragraph("Fine 1–3 mm nodules uniformly throughout both lungs", TABLE_B),
Paragraph("Miliary TB, haematogenous metastases, sarcoidosis, fungal infection", TABLE_B),
Paragraph("'Snow storm' appearance", TABLE_B)],
[Paragraph("Cavitation", TABLE_B),
Paragraph("Opacity with central lucency (air-fluid level if infected)", TABLE_B),
Paragraph("TB, lung abscess, Klebsiella, squamous cell carcinoma, Wegener's, fungal", TABLE_B),
Paragraph("Thick wall = malignancy/abscess; thin wall = hydatid/bulla", TABLE_B)],
[Paragraph("Mass / Nodule", TABLE_B),
Paragraph("Rounded opacity; > 3 cm = mass; ≤ 3 cm = nodule; ≤ 1 cm = micronodule", TABLE_B),
Paragraph("Primary lung cancer, metastasis, carcinoid, hamartoma, abscess, granuloma", TABLE_B),
Paragraph("Spiculated margins → malignancy; calcified smooth → benign", TABLE_B)],
[Paragraph("Pulmonary Oedema", TABLE_B),
Paragraph("'Bat-wing' / 'butterfly' perihilar haze, Kerley B lines, cephalization", TABLE_B),
Paragraph("Cardiogenic (CCF, MI), ARDS, fluid overload, neurogenic oedema", TABLE_B),
Paragraph("Cardiomegaly + effusions → cardiogenic", TABLE_B)],
[Paragraph("Hyperinflation", TABLE_B),
Paragraph("Flat diaphragms, barrel chest, > 10 posterior ribs, increased AP diameter", TABLE_B),
Paragraph("COPD (emphysema), severe asthma attack", TABLE_B),
Paragraph("Bullae may be visible in emphysema", TABLE_B)],
]
story.append(box_table(pattern_data, [30*mm, 43*mm, 60*mm, 37*mm]))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5 — SILHOUETTE SIGN
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("5. THE SILHOUETTE SIGN", TEAL))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"The silhouette sign occurs when an opacity lies in the <b>same plane</b> as a cardiac/mediastinal border, "
"causing that border to disappear. It localises the lesion to a specific lobe or segment.",
BODY))
story.append(Spacer(1, 2*mm))
sil_data = [
[Paragraph("Border Lost", TABLE_H), Paragraph("Structure Touching It", TABLE_H),
Paragraph("Lobe / Segment Involved", TABLE_H), Paragraph("Common Cause", TABLE_H)],
[Paragraph("Right heart border", TABLE_B), Paragraph("Right heart", TABLE_B),
Paragraph("Right middle lobe (RML)", TABLE_B),
Paragraph("RML pneumonia / collapse", TABLE_B)],
[Paragraph("Left heart border", TABLE_B), Paragraph("Left ventricle", TABLE_B),
Paragraph("Lingula (left upper lobe)", TABLE_B),
Paragraph("Lingular pneumonia", TABLE_B)],
[Paragraph("Right hemidiaphragm", TABLE_B), Paragraph("Diaphragm", TABLE_B),
Paragraph("Right lower lobe (RLL)", TABLE_B),
Paragraph("RLL consolidation / effusion", TABLE_B)],
[Paragraph("Left hemidiaphragm", TABLE_B), Paragraph("Diaphragm", TABLE_B),
Paragraph("Left lower lobe (LLL)", TABLE_B),
Paragraph("LLL consolidation", TABLE_B)],
[Paragraph("Aortic knob", TABLE_B), Paragraph("Aorta", TABLE_B),
Paragraph("Apical left upper lobe", TABLE_B),
Paragraph("LUL apical consolidation", TABLE_B)],
]
story.append(box_table(sil_data, [42*mm, 38*mm, 50*mm, 40*mm], header_colour=TEAL))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 6 — LOBAR COLLAPSE PATTERNS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("6. LOBAR COLLAPSE PATTERNS"))
story.append(Spacer(1, 3*mm))
collapse_data = [
[Paragraph("Lobe", TABLE_H), Paragraph("Trachea / Mediastinum", TABLE_H),
Paragraph("Fissure Movement", TABLE_H), Paragraph("Opacity Location", TABLE_H)],
[Paragraph("Right Upper Lobe (RUL)", TABLE_B),
Paragraph("Trachea deviated right; elevated right hilum", TABLE_B),
Paragraph("Minor fissure moves up; major fissure moves up", TABLE_B),
Paragraph("Right upper zone — 'S-sign of Golden' if mass", TABLE_B)],
[Paragraph("Right Middle Lobe (RML)", TABLE_B),
Paragraph("Minimal shift", TABLE_B),
Paragraph("Minor fissure moves down", TABLE_B),
Paragraph("Triangular opacity at right heart border; loses right heart border (silhouette sign)", TABLE_B)],
[Paragraph("Right Lower Lobe (RLL)", TABLE_B),
Paragraph("Mediastinum may shift right", TABLE_B),
Paragraph("Major fissure moves posteriorly / inferiorly", TABLE_B),
Paragraph("Triangular opacity behind right heart, obscures right hemidiaphragm", TABLE_B)],
[Paragraph("Left Upper Lobe (LUL)", TABLE_B),
Paragraph("Trachea may shift left", TABLE_B),
Paragraph("Major fissure moves anteriorly", TABLE_B),
Paragraph("Veil-like haze over left hemithorax; 'Luftsichel' sign (air crescent around aortic knob)", TABLE_B)],
[Paragraph("Left Lower Lobe (LLL)", TABLE_B),
Paragraph("Mediastinum may shift left", TABLE_B),
Paragraph("Major fissure moves posteriorly", TABLE_B),
Paragraph("Triangular opacity behind the heart; 'sail sign'; obscures left hemidiaphragm", TABLE_B)],
]
story.append(box_table(collapse_data, [32*mm, 42*mm, 45*mm, 51*mm]))
story.append(Spacer(1, 5*mm))
# PAGE BREAK
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════
# SECTION 7 — LINES & TUBES
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("7. LINES, TUBES & DEVICES — CORRECT POSITIONS", TEAL))
story.append(Spacer(1, 3*mm))
tubes_data = [
[Paragraph("Device", TABLE_H), Paragraph("Correct Position", TABLE_H),
Paragraph("Malposition Signs", TABLE_H)],
[Paragraph("Endotracheal Tube (ETT)", TABLE_B),
Paragraph("Tip 3–5 cm above carina; at level of T3–T4 with head neutral", TABLE_B),
Paragraph("Too low → right mainstem intubation (right lung hyperinflated, left collapsed). Too high → extubation risk.", TABLE_B)],
[Paragraph("Central Venous Catheter (CVC)", TABLE_B),
Paragraph("Tip at cavoatrial junction or distal SVC (T4–T6 level)", TABLE_B),
Paragraph("Too far → into right ventricle (arrhythmia risk). Pneumothorax on ipsilateral side.", TABLE_B)],
[Paragraph("Chest Drain / Intercostal Drain", TABLE_B),
Paragraph("Apical for pneumothorax; basal for effusion. Tip in pleural space", TABLE_B),
Paragraph("Tip in fissure → poor drainage. Kinked tube. Subcutaneous emphysema.", TABLE_B)],
[Paragraph("Nasogastric Tube (NGT)", TABLE_B),
Paragraph("Tip in stomach, below left hemidiaphragm, on the left side of midline", TABLE_B),
Paragraph("Coiling in oesophagus. Tip in right lung → risk of pulmonary instillation.", TABLE_B)],
[Paragraph("Pacemaker / AICD leads", TABLE_B),
Paragraph("RA lead in right atrial appendage; RV lead at RV apex; LV lead via coronary sinus", TABLE_B),
Paragraph("Lead fracture, displacement, perforation into pericardium.", TABLE_B)],
[Paragraph("PICC Line", TABLE_B),
Paragraph("Tip at cavoatrial junction (same as CVC)", TABLE_B),
Paragraph("Tip in axillary/subclavian vein is malpositioned. Tip too far into RV.", TABLE_B)],
]
story.append(box_table(tubes_data, [38*mm, 64*mm, 68*mm], header_colour=TEAL))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 8 — SPECIFIC DIAGNOSES AT A GLANCE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("8. SPECIFIC DIAGNOSES AT A GLANCE"))
story.append(Spacer(1, 3*mm))
diag_data = [
[Paragraph("Diagnosis", TABLE_H), Paragraph("Classic CXR Features", TABLE_H),
Paragraph("Key Differentiating Point", TABLE_H)],
[Paragraph("Community-acquired Pneumonia", TABLE_B),
Paragraph("Lobar / segmental consolidation with air bronchogram; unilateral", TABLE_B),
Paragraph("Silhouette sign localises lobe; Strep. pneumoniae = lobar, Mycoplasma = bilateral interstitial", TABLE_B)],
[Paragraph("Pulmonary Oedema (CCF)", TABLE_B),
Paragraph("Cardiomegaly, perihilar haze, Kerley B lines, pleural effusions (bilateral), upper lobe blood diversion (cephalization)", TABLE_B),
Paragraph("'ABCDE' of CCF: Alveolar oedema, Kerley B lines, Cardiomegaly, Dilated upper lobes, Effusions", TABLE_B)],
[Paragraph("ARDS", TABLE_B),
Paragraph("Bilateral diffuse alveolar infiltrates (ground-glass); no cardiomegaly; no Kerley B lines", TABLE_B),
Paragraph("Unlike CCF: no cardiomegaly, no Kerley B, history of sepsis/trauma", TABLE_B)],
[Paragraph("Pulmonary Embolism (PE)", TABLE_B),
Paragraph("Often normal. Hampton's hump (wedge-shaped pleural opacity). Westermark sign (oligaemia). Palla's sign (dilated PA).", TABLE_B),
Paragraph("CXR cannot exclude PE — CTPA is required", TABLE_B)],
[Paragraph("Sarcoidosis", TABLE_B),
Paragraph("Bilateral hilar lymphadenopathy (BHL) ± parenchymal infiltrates; Stage I = BHL only", TABLE_B),
Paragraph("Symmetrical BHL is almost pathognomonic; ACE level elevated", TABLE_B)],
[Paragraph("Tuberculosis (Primary)", TABLE_B),
Paragraph("Ghon focus (lower/mid zone) + hilar node = Ranke complex; pleural effusion; no cavitation", TABLE_B),
Paragraph("Primary TB: lower zone opacity + unilateral hilar LN", TABLE_B)],
[Paragraph("Tuberculosis (Post-primary)", TABLE_B),
Paragraph("Upper lobe cavitation, fibrosis, patchy consolidation; bilateral; old calcified nodules", TABLE_B),
Paragraph("Upper lobe cavity: always think post-primary TB until proven otherwise", TABLE_B)],
[Paragraph("Tension Pneumothorax", TABLE_B),
Paragraph("Tracheal deviation away from affected side; complete lung collapse; depressed ipsilateral diaphragm", TABLE_B),
Paragraph("Medical emergency — do NOT wait for CT; needle decompression first", TABLE_B)],
[Paragraph("Aortic Dissection", TABLE_B),
Paragraph("Widened mediastinum > 8 cm; indistinct aortic knob; left pleural effusion; tracheal deviation right", TABLE_B),
Paragraph("Any mediastinal widening in chest pain → urgent CT aortogram", TABLE_B)],
[Paragraph("Mesothelioma", TABLE_B),
Paragraph("Unilateral pleural thickening / lobulated pleural mass; pleural effusion; volume loss on affected side", TABLE_B),
Paragraph("Unlike effusion: pleural thickening persists after drainage; asbestos history", TABLE_B)],
[Paragraph("Lung Cancer", TABLE_B),
Paragraph("Hilar mass, peripheral nodule/mass, mediastinal widening, collapse, unilateral effusion, rib destruction", TABLE_B),
Paragraph("Any new mass > 3 cm, cavitating lesion, or Pancoast (apical) tumour → urgent CT + bronchoscopy", TABLE_B)],
]
story.append(box_table(diag_data, [40*mm, 74*mm, 56*mm]))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 9 — QUICK PATTERN MATCHING MNEMONICS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("9. QUICK MNEMONICS", TEAL))
story.append(Spacer(1, 3*mm))
mnem_left = [
["RIPE", "Technical Adequacy", "Rotation, Inspiration, Projection, Exposure"],
["ABCDE", "Systematic Review", "Airway, Breathing (Lungs), Cardiac/Mediastinum, Diaphragm, Everything Else"],
["5 T's", "Superior Mediastinal Mass", "Thymoma, Teratoma, Terrible lymphoma, Thyroid, Thoracic aortic aneurysm"],
["CT ratio", "Cardiomegaly", "≥ 0.5 on PA = cardiomegaly"],
["Hampton's Hump", "PE", "Wedge-shaped pleural-based opacity in PE"],
["Luftsichel", "LUL Collapse", "Air crescent around aortic knob in left upper lobe collapse"],
["S-sign of Golden", "RUL Collapse + Mass", "S-shaped minor fissure — hilar mass causing RUL collapse"],
["Westermark sign", "PE", "Focal oligaemia (reduced vascular markings) distal to PE"],
]
mnem_data = [[Paragraph("Mnemonic / Sign", TABLE_H), Paragraph("Used For", TABLE_H), Paragraph("Meaning", TABLE_H)]]
for row in mnem_left:
mnem_data.append([Paragraph(f"<b>{row[0]}</b>", TABLE_B), Paragraph(row[1], TABLE_B), Paragraph(row[2], TABLE_B)])
story.append(box_table(mnem_data, [38*mm, 40*mm, 92*mm], header_colour=TEAL))
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 10 — EMERGENCY FINDINGS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("10. DO NOT MISS — EMERGENCY CXR FINDINGS", colors.HexColor("#8B0000")))
story.append(Spacer(1, 3*mm))
emerg_data = [
[Paragraph("Finding", TABLE_H), Paragraph("CXR Sign", TABLE_H),
Paragraph("Immediate Action", TABLE_H)],
[Paragraph("Tension Pneumothorax", TABLE_B),
Paragraph("Tracheal deviation away, absent lung markings, depressed diaphragm", TABLE_B),
Paragraph("Needle decompression 2nd ICS MCL immediately", TABLE_B)],
[Paragraph("Massive Haemothorax", TABLE_B),
Paragraph("Unilateral complete opacification, trachea deviates away", TABLE_B),
Paragraph("Large-bore chest drain, cross-match, surgical review", TABLE_B)],
[Paragraph("Aortic Dissection", TABLE_B),
Paragraph("Widened mediastinum, blurred aortic knob, left haemothorax", TABLE_B),
Paragraph("Urgent CT aortogram, cardiothoracic surgery consult", TABLE_B)],
[Paragraph("ETT in Right Mainstem", TABLE_B),
Paragraph("Tip below carina level, left lung collapse", TABLE_B),
Paragraph("Withdraw ETT, recheck position", TABLE_B)],
[Paragraph("Free Air Under Diaphragm", TABLE_B),
Paragraph("Crescentic lucency below right hemidiaphragm (erect film)", TABLE_B),
Paragraph("Perforated viscus — urgent surgical review", TABLE_B)],
[Paragraph("Malpositioned NGT", TABLE_B),
Paragraph("Tip in lung, or coiling in oesophagus", TABLE_B),
Paragraph("Do NOT feed — resite tube", TABLE_B)],
]
t_emerg = Table(emerg_data, colWidths=[40*mm, 70*mm, 60*mm], repeatRows=1)
t_emerg.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#8B0000")),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 9),
("ALIGN", (0,0), (-1,0), "CENTER"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, GREY_LINE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("BACKGROUND", (0,1), (-1,-1), LIGHT_ORANGE),
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 8.5),
]))
story.append(t_emerg)
story.append(Spacer(1, 5*mm))
# ════════════════════════════════════════════════════════════════════════════
# FOOTER NOTE
# ════════════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width=W, thickness=0.5, color=GREY_LINE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"This quick reference is intended as a clinical aide-mémoire for healthcare professionals. "
"All radiological findings must be interpreted in clinical context. "
"Always correlate with patient history, examination, and additional investigations. "
"This document does not replace formal radiological reporting.",
SMALL))
# ── Build ─────────────────────────────────────────────────────────────────────
def add_page_number(canvas, doc):
canvas.saveState()
canvas.setFont("Helvetica", 8)
canvas.setFillColor(colors.grey)
page_num = canvas.getPageNumber()
canvas.drawCentredString(A4[0]/2, 8*mm, f"CXR Quick Reference · Page {page_num}")
canvas.restoreState()
doc.build(story, onFirstPage=add_page_number, onLaterPages=add_page_number)
print(f"PDF created: {OUTPUT}")
cd /tmp/workspace/cxr-reference && python make_cxr_pdf.py 2>&1
ls -lh /tmp/workspace/cxr-reference/CXR_Quick_Reference.pdf
CXR Quick Reference
PDF Document · PDF
| Section | Content |
|---|---|
| 1. RIPE Checklist | Technical adequacy - Rotation, Inspiration, Projection, Exposure |
| 2. ABCDE Approach | Systematic review framework for every CXR |
| 3. Key Measurements | CT ratio, mediastinal width, carina angle, diaphragm heights with normal values |
| 4. Opacity Patterns | Consolidation, collapse, effusion, pneumothorax, interstitial, miliary, cavitation, mass, pulmonary oedema, hyperinflation |
| 5. Silhouette Sign | Which border loss localises which lobe |
| 6. Lobar Collapse | Trachea shift, fissure movement, and opacity location for all 5 lobes |
| 7. Lines & Tubes | Correct positions and malposition signs for ETT, CVC, NGT, chest drain, pacemaker, PICC |
| 8. Specific Diagnoses | Classic features of 11 conditions (pneumonia, CCF, ARDS, PE, sarcoidosis, TB, tension pneumothorax, aortic dissection, mesothelioma, lung cancer) |
| 9. Mnemonics | RIPE, ABCDE, Hampton's Hump, Luftsichel, S-sign of Golden, Westermark, and more |
| 10. Don't Miss | Emergency findings requiring immediate action (highlighted in red) |
Histology slides Of thyroid Duodenum pancreas hyaline

| Structure | Appearance | Function |
|---|---|---|
| Follicles | Spherical spaces ~30 µm diameter; ~20-40 per lobule | Structural unit of the thyroid |
| Colloid | Homogeneous, pale eosinophilic (pink) material filling the follicle lumen | Storage form of thyroglobulin (inactive T3/T4) |
| Follicular cells (thyrocytes) | Single layer of cuboidal to low columnar cells lining each follicle | Synthesise T3 and T4 |
| Parafollicular (C) cells | Larger pale cells scattered in interfollicular stroma, especially upper poles | Secrete calcitonin (calcium regulation) |
| Fibrous septa | Thin connective tissue dividing the gland into lobules | Support and vascularity |

| Layer | Structure | Appearance | Significance |
|---|---|---|---|
| Mucosa | Villi | Tall finger-like projections lined by columnar absorptive cells + goblet cells | Massively increases surface area for absorption |
| Mucosa | Crypts of Lieberkühn | Tubular glands at base of villi | Contain stem cells, Paneth cells, enteroendocrine cells |
| Mucosa | Muscularis mucosae | Thin smooth muscle layer | Separates mucosa from submucosa |
| Submucosa | Brunner glands | Branched tubular mucous glands - UNIQUE TO DUODENUM | Secrete alkaline (pH 8.1-9.3) mucus + bicarbonate - neutralise gastric acid chyme |
| Muscularis externa | Inner circular + outer longitudinal smooth muscle | Two muscle layers | Peristalsis + segmentation |



| Component | Appearance | Cell Types | Function |
|---|---|---|---|
| Exocrine acini | Rounded clusters of pyramidal cells; basophilic basal cytoplasm (rER), eosinophilic apical zymogen granules | Acinar cells (serous); centroacinar cells | Digest proteins, fats, carbohydrates, nucleic acids |
| Centroacinar cells | Small pale cells at the centre of acinus where duct begins | Special to pancreas | Line intercalated ducts; secrete HCO₃⁻ |
| Intercalated ducts | Very thin ducts originating inside the acinus - unique to pancreas | Duct epithelium | Drain acini; add bicarbonate |
| Islets of Langerhans | Pale, rounded clusters 1-3 million throughout pancreas (~1-2% volume); well-vascularised by fenestrated capillaries | α cells (glucagon), β cells (insulin), δ cells (somatostatin), PP cells | Blood glucose regulation |

| Structure | Appearance | Significance |
|---|---|---|
| Matrix | Homogeneous, glassy blue-purple (H&E) - appears "hyaline" | Type II collagen + proteoglycans + water - shock absorption |
| Chondrocytes | Round cells in lacunae (spaces); may appear in isogenous groups | Produce and maintain matrix |
| Perichondrium | Dense fibrous layer surrounding cartilage | Appositional growth, blood supply (cartilage is avascular) |
| Lacunae | Empty-appearing spaces around chondrocytes | Shrinkage artifact from fixation |
| Location | What it Means | Associated Condition |
|---|---|---|
| Arteriole walls | Hyaline arteriolosclerosis | Hypertension, diabetes mellitus |
| Intracellular (hepatocytes) | Alcoholic (Mallory) hyaline | Alcoholic liver disease |
| Renal tubular droplets | Reabsorption droplets | Nephrotic syndrome |
| Russell bodies (plasma cells) | Immunoglobulin accumulation | Chronic inflammation |
| Old scars/tendons | Hyalinised collagen fibers | Post-scar remodelling |
| Amyloid | Congo red + apple-green birefringence | Amyloidosis |
According to Robbins Pathologic Basis of Disease: "The term hyaline usually refers to an alteration within cells or in the extracellular space that gives a homogeneous, glassy, pink appearance in routine H&E sections. It is widely used as a descriptive histologic term rather than a specific marker for cell injury."