Create a quick reference pdf of this in easy language from standard book

Reading File
Reading File
Running Command

ls "/home/daytona/workspace/attachments/5ff6b6ba-e96a-485d-add6-00541dc7db24/"

Running Command

python3 -c " import pdfplumber path = '/home/daytona/workspace/attachments/5ff6b6ba-e96a-485d-add6-00541dc7db24/Here is a much more detailed easy-to-understand ex.pdf' with pdfplumber.open(path) as pdf: print(f'Total pages: {len(pdf.pages)}') for i, page in enumerate(pdf.pages): print(f'--- PAGE {i+1} ---') text = page.extract_text() if text: print(text[:3000]) print() "

Running Command

mkdir -p /home/daytona/workspace/blood-transfusion-qr

Writing File

~/blood-transfusion-qr/make_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 cm, mm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import PageBreak

OUTPUT = "/home/daytona/workspace/blood-transfusion-qr/Blood_Transfusion_Quick_Reference.pdf"

# ── Colour palette ──────────────────────────────────────────────
C_DARK   = colors.HexColor("#1a237e")   # deep navy
C_MID    = colors.HexColor("#283593")   # mid navy
C_LIGHT  = colors.HexColor("#e8eaf6")   # very light blue
C_ACCENT = colors.HexColor("#c62828")   # red accent
C_GREEN  = colors.HexColor("#1b5e20")   # dark green
C_YELLOW = colors.HexColor("#fff8e1")   # pale yellow
C_ORANGE = colors.HexColor("#e65100")   # orange
C_TEAL   = colors.HexColor("#006064")
C_WHITE  = colors.white
C_GREY   = colors.HexColor("#f5f5f5")
C_BORDER = colors.HexColor("#9fa8da")

W, H = A4  # 595 x 842

# ── Styles ───────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def s(name, **kw):
    base = styles["Normal"]
    return ParagraphStyle(name, parent=base, **kw)

TITLE   = s("TITLE",   fontSize=22, textColor=C_WHITE,  alignment=TA_CENTER,
            fontName="Helvetica-Bold", leading=28, spaceAfter=4)
SUB     = s("SUB",     fontSize=11, textColor=C_WHITE,  alignment=TA_CENTER,
            fontName="Helvetica", leading=14)
H1      = s("H1",      fontSize=13, textColor=C_WHITE,  fontName="Helvetica-Bold",
            leading=16, spaceBefore=2, spaceAfter=2)
H2      = s("H2",      fontSize=10, textColor=C_DARK,   fontName="Helvetica-Bold",
            leading=13, spaceBefore=6, spaceAfter=2)
BODY    = s("BODY",    fontSize=8.5, textColor=colors.black,
            fontName="Helvetica", leading=12, spaceAfter=2)
SMALL   = s("SMALL",   fontSize=7.5, textColor=colors.HexColor("#333333"),
            fontName="Helvetica", leading=10)
BOLD    = s("BOLD",    fontSize=8.5, textColor=colors.black,
            fontName="Helvetica-Bold", leading=12)
RED     = s("RED",     fontSize=8.5, textColor=C_ACCENT,
            fontName="Helvetica-Bold", leading=12)
GREEN   = s("GREEN",   fontSize=8.5, textColor=C_GREEN,
            fontName="Helvetica-Bold", leading=12)
TH      = s("TH",      fontSize=8,   textColor=C_WHITE, fontName="Helvetica-Bold",
            alignment=TA_CENTER, leading=10)
TD      = s("TD",      fontSize=8,   textColor=colors.black,
            fontName="Helvetica", leading=10)
TDSMALL = s("TDSMALL", fontSize=7.5, textColor=colors.black,
            fontName="Helvetica", leading=10)
FOOTER  = s("FOOTER",  fontSize=7,   textColor=colors.grey,
            alignment=TA_CENTER, leading=9)
MEMBOX  = s("MEMBOX",  fontSize=8.5, textColor=C_ORANGE,
            fontName="Helvetica-BoldOblique", leading=12, alignment=TA_CENTER)

# ── Helper: section header block ────────────────────────────────
def section_header(text, bg=C_DARK):
    data = [[Paragraph(text, H1)]]
    t = Table(data, colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("ROUNDEDCORNERS", [4]),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ]))
    return t

def mini_header(text, bg=C_MID):
    data = [[Paragraph(text, H2)]]
    t = Table(data, colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ]))
    t2 = Table([[t]], colWidths=[W - 3*cm])
    # just return the inner table, override H2 text color
    return t

def colored_mini(text, bg=C_LIGHT, fg=C_DARK):
    p = ParagraphStyle("cm", parent=H2, textColor=fg)
    data = [[Paragraph(text, p)]]
    t = Table(data, colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("LINEBELOW", (0,0), (-1,-1), 0.5, C_BORDER),
    ]))
    return t

# ── Table style helpers ──────────────────────────────────────────
TSTYLE_BASE = [
    ("FONTNAME",      (0,0), (-1,0),  "Helvetica-Bold"),
    ("FONTSIZE",      (0,0), (-1,-1), 8),
    ("BACKGROUND",    (0,0), (-1,0),  C_DARK),
    ("TEXTCOLOR",     (0,0), (-1,0),  C_WHITE),
    ("ALIGN",         (0,0), (-1,-1), "CENTER"),
    ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [C_WHITE, C_GREY]),
    ("GRID",          (0,0), (-1,-1), 0.4, C_BORDER),
    ("TOPPADDING",    (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("LEFTPADDING",   (0,0), (-1,-1), 4),
    ("RIGHTPADDING",  (0,0), (-1,-1), 4),
]

def make_table(data, col_widths, extra_styles=None):
    t = Table(data, colWidths=col_widths, repeatRows=1)
    ts = list(TSTYLE_BASE)
    if extra_styles:
        ts.extend(extra_styles)
    t.setStyle(TableStyle(ts))
    return t

# ── Page template with header/footer ────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    # top stripe
    canvas.setFillColor(C_DARK)
    canvas.rect(0, H - 18, W, 18, fill=1, stroke=0)
    canvas.setFillColor(C_WHITE)
    canvas.setFont("Helvetica-Bold", 9)
    canvas.drawCentredString(W/2, H - 12, "BLOOD TRANSFUSION – QUICK REFERENCE  |  Goldman-Cecil · Bailey & Love · Barash · Henry's · Scott-Brown")
    # bottom
    canvas.setFillColor(C_DARK)
    canvas.rect(0, 0, W, 14, fill=1, stroke=0)
    canvas.setFillColor(C_WHITE)
    canvas.setFont("Helvetica", 7)
    canvas.drawString(1.5*cm, 4, "For educational use only – not a substitute for clinical judgment")
    canvas.drawRightString(W - 1.5*cm, 4, f"Page {doc.page}")
    canvas.restoreState()

# ── Build document ───────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT, pagesize=A4,
    leftMargin=1.5*cm, rightMargin=1.5*cm,
    topMargin=1.8*cm, bottomMargin=1.3*cm,
)

story = []
SP = lambda n=4: Spacer(1, n)

# ═══════════════════════════════════════════════════════════════
# COVER / TITLE BLOCK
# ═══════════════════════════════════════════════════════════════
cover = Table(
    [[Paragraph("BLOOD TRANSFUSION", TITLE)],
     [Paragraph("Quick Reference Guide  •  Easy Language Summary", SUB)],
     [Paragraph("Goldman-Cecil Medicine  •  Bailey & Love  •  Barash Anesthesia  •  Henry's Laboratory Methods  •  Scott-Brown", SUB)]],
    colWidths=[W - 3*cm]
)
cover.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,-1), C_DARK),
    ("TOPPADDING",    (0,0), (-1,-1), 10),
    ("BOTTOMPADDING", (0,0), (-1,-1), 10),
    ("LEFTPADDING",   (0,0), (-1,-1), 10),
    ("RIGHTPADDING",  (0,0), (-1,-1), 10),
    ("LINEBELOW",     (0,2), (-1,2), 3, C_ACCENT),
]))
story.append(cover)
story.append(SP(10))

# ═══════════════════════════════════════════════════════════════
# SECTION 1 – BLOOD PRODUCTS AT A GLANCE
# ═══════════════════════════════════════════════════════════════
story.append(section_header("① BLOOD PRODUCTS AT A GLANCE"))
story.append(SP(5))

products_hdr = ["Product", "Key Contents", "Storage", "Shelf Life", "Effect / Use"]
products_data = [
    [Paragraph(h, TH) for h in products_hdr],
    [Paragraph("Whole Blood", BOLD),
     Paragraph("RBCs + platelets + clotting factors", TD),
     Paragraph("2–6 °C", TD),
     Paragraph("~3 weeks", TD),
     Paragraph("Trauma / military – coagulation-factor rich", TD)],
    [Paragraph("PRBC\n(Packed Red Cells)", BOLD),
     Paragraph("Concentrated red cells; Hct 50–70%", TD),
     Paragraph("2–6 °C\n(SAG-M additive)", TD),
     Paragraph("6 weeks", TD),
     Paragraph("Anaemia. 1 unit → Hb ↑1 g/dL; Hct ↑3%\nVolume ~330 mL/unit", TD)],
    [Paragraph("Platelets", BOLD),
     Paragraph("~250×10⁹/L\n(pooled from 4–6 donors)", TD),
     Paragraph("20–24 °C\non agitator", TD),
     Paragraph("5 days\n(shortest!)", TD),
     Paragraph("Low platelet count or dysfunction. 1 unit → plt ↑5,000–10,000/µL", TD)],
    [Paragraph("FFP\n(Fresh Frozen Plasma)", BOLD),
     Paragraph("All clotting factors (I,II,V,VII,VIII,IX,X,XI) + fibrinogen", TD),
     Paragraph("−40 to −50 °C", TD),
     Paragraph("2 years frozen;\nuse within 20 min of thaw", TD),
     Paragraph("Haemorrhage + coagulopathy; warfarin reversal; massive transfusion (1:1:1); liver disease", TD)],
    [Paragraph("Cryoprecipitate", BOLD),
     Paragraph("Factor VIII, XIII\nFibrinogen\nvon Willebrand Factor\n(memory: '8-13-F-vW')", TD),
     Paragraph("−30 °C", TD),
     Paragraph("2 years", TD),
     Paragraph("Low fibrinogen (first factor to fall in haemorrhage); Haemophilia A; vWD", TD)],
    [Paragraph("PCC\n(Prothrombin Complex Concentrate)", BOLD),
     Paragraph("Factors II, VII, IX, X\n(vitamin K-dependent)", TD),
     Paragraph("Room temp\n(lyophilised)", TD),
     Paragraph("Long shelf life", TD),
     Paragraph("BEST for urgent warfarin reversal.\nSmall volume (~50 mL) vs litres of FFP. Works in minutes.", TD)],
]
story.append(make_table(products_data, [2.2*cm, 4.2*cm, 2.8*cm, 2.8*cm, 5.6*cm]))
story.append(SP(4))

# ═══════════════════════════════════════════════════════════════
# SECTION 2 – STORAGE SOLUTIONS
# ═══════════════════════════════════════════════════════════════
story.append(section_header("② STORAGE SOLUTIONS (CPDA / SAG-M)", bg=C_TEAL))
story.append(SP(5))

store_hdr = ["Component", "Role", "Why It Matters"]
store_data = [
    [Paragraph(h, TH) for h in store_hdr],
    [Paragraph("Citrate", BOLD), Paragraph("Anticoagulant – binds Ca²⁺", TD),
     Paragraph("Blood can't clot without calcium → blood stays liquid in bag", TD)],
    [Paragraph("Phosphate", BOLD), Paragraph("Buffer; raises 2,3-BPG", TD),
     Paragraph("Prevents acidosis; improves O₂ offloading to tissues", TD)],
    [Paragraph("Dextrose (glucose)", BOLD), Paragraph("Energy source for RBCs", TD),
     Paragraph("RBCs use glycolysis only; glucose → ATP → keeps cells alive", TD)],
    [Paragraph("Adenine", BOLD), Paragraph("ATP synthesis", TD),
     Paragraph("Replenishes ATP; CPDA lasts 5 wk vs CPD 2–3 wk without it", TD)],
    [Paragraph("Saline (SAG-M)", BOLD), Paragraph("Diluent", TD),
     Paragraph("Reduces viscosity so blood flows through giving sets", TD)],
    [Paragraph("Mannitol (SAG-M)", BOLD), Paragraph("Osmotic stabiliser", TD),
     Paragraph("Prevents red cell haemolysis (cells bursting)", TD)],
]
story.append(make_table(store_data, [3.5*cm, 5*cm, 9.1*cm]))
story.append(SP(3))
story.append(Paragraph(
    "<b>Note:</b> SAG-M is an <b>additive</b> (added after plasma removed from CPD blood). Together they give a 6-week shelf life.",
    BODY))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 3 – TRANSFUSION TRIGGERS
# ═══════════════════════════════════════════════════════════════
story.append(section_header("③ WHEN TO TRANSFUSE – Hb THRESHOLDS", bg=C_ACCENT))
story.append(SP(5))

trig_hdr = ["Clinical Situation", "Transfuse When Hb <", "Key Principle"]
trig_data = [
    [Paragraph(h, TH) for h in trig_hdr],
    [Paragraph("Stable, not bleeding, no surgery", TD),
     Paragraph("6 g/dL", BOLD),
     Paragraph("Resting body compensates well even at low Hb", TD)],
    [Paragraph("Actively bleeding / symptomatic / pre-op", TD),
     Paragraph("8 g/dL", BOLD),
     Paragraph("Higher threshold – patient may not tolerate low Hb", TD)],
    [Paragraph("Cardiac ischaemia", TD),
     Paragraph("8–10 g/dL", BOLD),
     Paragraph("Heart is very sensitive to low oxygen delivery", TD)],
    [Paragraph("Septic shock", TD),
     Paragraph("7 g/dL", BOLD),
     Paragraph("TRICC trial: same outcome as 9 g/dL threshold", TD)],
]
story.append(make_table(trig_data, [6*cm, 3*cm, 8.6*cm]))
story.append(SP(3))
story.append(Paragraph(
    "The <b>TRICC trial</b> proved that liberal transfusion (target Hb > 10 g/dL) increased morbidity and mortality. Less is more.",
    BODY))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 4 – MASSIVE TRANSFUSION
# ═══════════════════════════════════════════════════════════════
story.append(section_header("④ MASSIVE TRANSFUSION", bg=C_GREEN))
story.append(SP(5))

story.append(colored_mini("Definition – ANY ONE of:", C_LIGHT, C_DARK))
story.append(SP(3))
def_data = [
    ["• Replacement of 1 entire blood volume (≈5 L in 70 kg adult) within 24 h"],
    ["• > 10 units PRBC in 24 hours"],
    ["• ≥ 3 units PRBC in 1 hour when ongoing need expected"],
    ["• Blood loss > 150 mL/minute"],
]
for row in def_data:
    story.append(Paragraph(row[0], BODY))
story.append(SP(6))

# Lethal Triad box
triad_data = [[
    Paragraph("❄ HYPOTHERMIA\n(cold stored blood)", BOLD),
    Paragraph("+", s("plus", fontSize=18, textColor=C_ACCENT, alignment=TA_CENTER, fontName="Helvetica-Bold")),
    Paragraph("⚗ ACIDOSIS\n(poor perfusion)", BOLD),
    Paragraph("+", s("plus", fontSize=18, textColor=C_ACCENT, alignment=TA_CENTER, fontName="Helvetica-Bold")),
    Paragraph("🩸 COAGULOPATHY\n(diluted factors)", BOLD),
]]
triad = Table(triad_data, colWidths=[4.2*cm, 0.8*cm, 4.2*cm, 0.8*cm, 4.2*cm])
triad.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (0,0), colors.HexColor("#e3f2fd")),
    ("BACKGROUND",    (2,0), (2,0), colors.HexColor("#fff3e0")),
    ("BACKGROUND",    (4,0), (4,0), colors.HexColor("#fce4ec")),
    ("ALIGN",         (0,0), (-1,-1), "CENTER"),
    ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ("TOPPADDING",    (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("BOX",           (0,0), (0,0), 1, colors.HexColor("#1565c0")),
    ("BOX",           (2,0), (2,0), 1, colors.HexColor("#e65100")),
    ("BOX",           (4,0), (4,0), 1, C_ACCENT),
    ("FONTNAME",      (1,0), (1,0), "Helvetica-Bold"),
    ("FONTNAME",      (3,0), (3,0), "Helvetica-Bold"),
]))
story.append(Paragraph("<b>THE LETHAL TRIAD</b> – each one makes the others worse:", BOLD))
story.append(SP(3))
story.append(triad)
story.append(SP(6))

story.append(Paragraph("<b>Management of Massive Transfusion:</b>", BOLD))
story.append(SP(2))
mt_data = [
    [Paragraph("Strategy", TH), Paragraph("What to Do", TH), Paragraph("Why", TH)],
    [Paragraph("1:1:1 Balanced\nTransfusion", BOLD),
     Paragraph("RBC : FFP : Platelets = 1 : 1 : 1 (from the very start)", TD),
     Paragraph("Mimics whole blood. Prevents dilutional coagulopathy. Reduces mortality.", TD)],
    [Paragraph("Tranexamic Acid\n(TXA)", BOLD),
     Paragraph("Give IV as early as possible, within 3 hours of injury", TD),
     Paragraph("Blocks plasmin from dissolving clots (antifibrinolytic). After 3 h it can worsen outcomes.", TD)],
    [Paragraph("Cryoprecipitate", BOLD),
     Paragraph("Give empirically – do NOT wait for lab results", TD),
     Paragraph("Fibrinogen is the FIRST clotting factor to fall critically. Clots can't form without it.", TD)],
    [Paragraph("Calcium", BOLD),
     Paragraph("IV calcium gluconate", TD),
     Paragraph("Citrate in stored blood binds Ca²⁺ → hypocalcaemia worsens coagulopathy and causes arrhythmia", TD)],
    [Paragraph("Blood Warmer", BOLD),
     Paragraph("Warm all blood products before infusion", TD),
     Paragraph("Cold blood worsens hypothermia → enzymes in coagulation cascade stop working", TD)],
    [Paragraph("TEG / ROTEM", BOLD),
     Paragraph("Point-of-care thromboelastometry at bedside", TD),
     Paragraph("Real-time clotting picture in minutes. Much faster than lab. Guides targeted replacement.", TD)],
]
story.append(make_table(mt_data, [3.2*cm, 6.2*cm, 8.2*cm]))
story.append(SP(6))

# ═══════════════════════════════════════════════════════════════
# SECTION 5 – COMPLICATIONS TABLE
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑤ COMPLICATIONS OF MASSIVE TRANSFUSION"))
story.append(SP(5))

comp_hdr = ["Complication", "Cause", "Signs / Symptoms", "Management"]
comp_data = [
    [Paragraph(h, TH) for h in comp_hdr],
    [Paragraph("Hypocalcaemia", BOLD),
     Paragraph("Citrate in stored blood binds patient's Ca²⁺", TDSMALL),
     Paragraph("Tingling, tetany, cardiac arrhythmia", TDSMALL),
     Paragraph("IV Calcium gluconate", TDSMALL)],
    [Paragraph("Hyperkalaemia", BOLD),
     Paragraph("RBCs leak K⁺ into storage solution over time", TDSMALL),
     Paragraph("Cardiac arrhythmia (especially with rapid infusion)", TDSMALL),
     Paragraph("Monitor ECG; treat arrhythmia", TDSMALL)],
    [Paragraph("Hypothermia", BOLD),
     Paragraph("Cold stored blood (2–6°C) infused rapidly", TDSMALL),
     Paragraph("Worsens coagulopathy + arrhythmia", TDSMALL),
     Paragraph("Blood warmers; warming blankets", TDSMALL)],
    [Paragraph("Coagulopathy\n(dilutional)", BOLD),
     Paragraph("Dilution of clotting factors + platelets from PRBC-only transfusion", TDSMALL),
     Paragraph("Bleeding from all sites (wound, cannula sites)", TDSMALL),
     Paragraph("1:1:1 ratio + cryoprecipitate + calcium", TDSMALL)],
    [Paragraph("Metabolic\nAlkalosis", BOLD),
     Paragraph("Citric acid in stored blood → metabolised to bicarbonate", TDSMALL),
     Paragraph("Usually after initial acidosis resolves", TDSMALL),
     Paragraph("Monitor ABG; usually self-correcting", TDSMALL)],
    [Paragraph("Iron Overload", BOLD),
     Paragraph("Each PRBC unit contains ~250 mg elemental iron", TDSMALL),
     Paragraph("Mainly in repeated transfusions (thalassaemia, aplastic anaemia)", TDSMALL),
     Paragraph("Chelation therapy (desferrioxamine)", TDSMALL)],
]
story.append(make_table(comp_data, [2.8*cm, 4.2*cm, 4.5*cm, 6.1*cm]))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 6 – TRANSFUSION REACTIONS
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑥ TRANSFUSION REACTIONS – Identification & Management", bg=C_ACCENT))
story.append(SP(5))

rx_hdr = ["Reaction", "Frequency", "Mechanism", "Key Signs", "Treatment"]
rx_data = [
    [Paragraph(h, TH) for h in rx_hdr],
    [Paragraph("Acute Haemolytic\n(AHTR) ⚠️", s("rb", parent=BOLD, textColor=C_ACCENT)),
     Paragraph("1 : 110,000\n(rare but deadly)", TDSMALL),
     Paragraph("ABO mismatch → pre-formed IgM → complement activation → intravascular haemolysis → DIC + AKI", TDSMALL),
     Paragraph("Awake: burning pain in vein, fever, back pain, oliguria, dark urine\nAnaesthetised: unexplained hypotension + bleeding from wound (DIC)", TDSMALL),
     Paragraph("1. STOP transfusion\n2. Keep IV open (saline)\n3. Recheck blood bank\n4. IV fluids + diuretics (furosemide/mannitol)\n5. Treat DIC + shock", TDSMALL)],
    [Paragraph("Febrile Non-\nHaemolytic (FNHTR)\n★ Most Common", BOLD),
     Paragraph("1 : 1,100\n(most common!)", TDSMALL),
     Paragraph("Recipient antibodies attack donor WBCs (HLA antigens); OR cytokines pre-formed during platelet storage", TDSMALL),
     Paragraph("Fever ≥1°C rise, chills, rigors, nausea – NO haemolysis, NO hypotension\nRule out AHTR first!", TDSMALL),
     Paragraph("Stop transfusion. Paracetamol. Usually self-limiting.\nPrevention: Leukoreduction (incidence ↓ from 30% → <1%)", TDSMALL)],
    [Paragraph("Allergic /\nAnaphylaxis", BOLD),
     Paragraph("1 : 1,200\n(urticaria)\nRare (anaphylaxis)", TDSMALL),
     Paragraph("IgE reaction to plasma proteins in donor product", TDSMALL),
     Paragraph("Mild: hives, itching\nModerate: angioedema, wheeze\nSevere: bronchospasm, hypotension\nIgA-deficient patients → anaphylaxis with any IgA-containing product", TDSMALL),
     Paragraph("Mild: antihistamines; may continue at slower rate\nSevere (anaphylaxis): STOP + Adrenaline 1:1,000 IM\nIgA-deficient: must use IgA-deficient blood", TDSMALL)],
    [Paragraph("TRALI\n(Lung injury)", s("rtr", parent=BOLD, textColor=C_ORANGE)),
     Paragraph("1 : 140,000\n(was leading cause of death)", TDSMALL),
     Paragraph("'Two-hit': Primed lung neutrophils (from illness) + donor anti-HLA antibodies (esp. from multiparous women) → massive lung inflammation → non-cardiogenic oedema", TDSMALL),
     Paragraph("Within 6 h: severe hypoxia, HYPOTENSION, bilateral infiltrates (snowstorm CXR), no heart failure signs", TDSMALL),
     Paragraph("STOP transfusion.\nHigh-flow O₂ ± mechanical ventilation.\nNO diuretics (permeability oedema).\nMost recover in 2–4 days. 5–10% mortality.", TDSMALL)],
    [Paragraph("TACO\n(Fluid overload)", BOLD),
     Paragraph("1 : 9,000\n(more common\nthan TRALI)", TDSMALL),
     Paragraph("Heart/kidneys can't handle extra volume → hydrostatic (cardiogenic) pulmonary oedema", TDSMALL),
     Paragraph("Dyspnoea, HYPERTENSION (key!), tachycardia, raised JVP, orthopnoea, raised BNP (>1000)", TDSMALL),
     Paragraph("Slow/stop transfusion.\nFurosemide (diuretics).\nSit upright.\nSupplemental O₂.", TDSMALL)],
]
story.append(make_table(rx_data, [2.8*cm, 2*cm, 4.2*cm, 4.8*cm, 3.8*cm]))
story.append(SP(5))

# TACO vs TRALI comparison
story.append(Paragraph("<b>TACO vs TRALI – Quick Comparison</b>", BOLD))
story.append(SP(3))
comp2_hdr = ["Feature", "TACO", "TRALI"]
comp2_data = [
    [Paragraph(h, TH) for h in comp2_hdr],
    [Paragraph("Mechanism", TD), Paragraph("Fluid overload", TD), Paragraph("Immune – anti-HLA antibodies", TD)],
    [Paragraph("Blood pressure", TD),
     Paragraph("⬆ HYPERTENSIVE", s("t1", parent=TD, textColor=C_ACCENT, fontName="Helvetica-Bold")),
     Paragraph("⬇ HYPOTENSIVE", s("t2", parent=TD, textColor=C_GREEN, fontName="Helvetica-Bold"))],
    [Paragraph("Oedema type", TD), Paragraph("Hydrostatic (cardiogenic)", TD), Paragraph("Permeability (non-cardiogenic)", TD)],
    [Paragraph("Onset", TD), Paragraph("2–6 h (up to 12 h)", TD), Paragraph("Within 6 h", TD)],
    [Paragraph("BNP", TD), Paragraph("Very elevated (>1000 pg/mL)", TD), Paragraph("Normal / mildly elevated", TD)],
    [Paragraph("CXR", TD), Paragraph("Cardiomegaly + bilateral oedema + pleural effusions", TD), Paragraph("Bilateral infiltrates, no cardiomegaly", TD)],
    [Paragraph("Treatment", TD),
     Paragraph("Diuretics ✔", s("t3", parent=TD, textColor=C_GREEN, fontName="Helvetica-Bold")),
     Paragraph("NO diuretics ✘", s("t4", parent=TD, textColor=C_ACCENT, fontName="Helvetica-Bold"))],
    [Paragraph("Mortality", TD), Paragraph("Good with treatment", TD), Paragraph("5–10%", TD)],
]
story.append(make_table(comp2_data, [3.5*cm, 7.5*cm, 6.6*cm]))
story.append(SP(4))

mem_box_data = [[Paragraph(
    "🧠 MEMORY TRICK: TACO = Too much fluid = Hypertension = Diuretics fix it   |   "
    "TRALI = Immune attack on lungs = Hypotension = NO diuretics", MEMBOX)]]
mem_box = Table(mem_box_data, colWidths=[W - 3*cm])
mem_box.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,-1), colors.HexColor("#fff8e1")),
    ("BOX",           (0,0), (-1,-1), 1.5, C_ORANGE),
    ("TOPPADDING",    (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
]))
story.append(mem_box)
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 7 – LEUKOREDUCTION
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑦ LEUKOREDUCTION (Leukodepletion)", bg=C_TEAL))
story.append(SP(5))

story.append(Paragraph("<b>What is it?</b> Filtering out WBCs from blood products before transfusion.", BODY))
story.append(SP(3))
leuko_hdr = ["Reason for Removing WBCs", "Effect"]
leuko_data = [
    [Paragraph(h, TH) for h in leuko_hdr],
    [Paragraph("Prevent Febrile Non-Haemolytic Reactions (FNHTR)", TD),
     Paragraph("Recipient antibodies attack donor WBCs → cytokines → fever. Removing WBCs eliminates the target. FNHTR ↓ from 30% → <1%", TD)],
    [Paragraph("Prevent vCJD transmission", TD),
     Paragraph("Prion proteins (variant Creutzfeldt-Jakob / 'mad cow') may be carried by WBCs. UK implemented universal leukodepletion in 1999 partly for this reason.", TD)],
    [Paragraph("Reduce immunogenicity / alloimmunisation", TD),
     Paragraph("Donor WBCs prime the recipient's immune system → antibodies that cause problems in future transfusions (harder to cross-match)", TD)],
]
story.append(make_table(leuko_data, [6.5*cm, 11.1*cm]))
story.append(SP(3))
story.append(Paragraph("<b>UK Policy:</b> Universal leukodepletion of ALL blood components since 1999.", BODY))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 8 – ABO BLOOD GROUPS & CROSS-MATCHING
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑧ ABO BLOOD GROUPS & CROSS-MATCHING"))
story.append(SP(5))

story.append(Paragraph("<b>Blood Group Summary:</b>", BOLD))
story.append(SP(3))
abo_hdr = ["Blood Group", "Antigens on RBC", "Antibodies in Plasma", "Role"]
abo_data = [
    [Paragraph(h, TH) for h in abo_hdr],
    [Paragraph("A", BOLD), Paragraph("A antigen", TD), Paragraph("Anti-B", TD), Paragraph("—", TD)],
    [Paragraph("B", BOLD), Paragraph("B antigen", TD), Paragraph("Anti-A", TD), Paragraph("—", TD)],
    [Paragraph("AB", BOLD), Paragraph("A and B antigens", TD), Paragraph("None", TD),
     Paragraph("Universal RECIPIENT (no antibodies to react)", s("gr", parent=TD, textColor=C_GREEN, fontName="Helvetica-Bold"))],
    [Paragraph("O", BOLD), Paragraph("None", TD), Paragraph("Anti-A AND Anti-B", TD),
     Paragraph("Universal DONOR (no antigens to react against)", s("gr2", parent=TD, textColor=C_GREEN, fontName="Helvetica-Bold"))],
]
story.append(make_table(abo_data, [3*cm, 4*cm, 4.5*cm, 6.1*cm]))
story.append(SP(6))

story.append(Paragraph("<b>Blood Availability in Emergency:</b>", BOLD))
story.append(SP(3))
avail_data = [
    [Paragraph("Option", TH), Paragraph("Time to Available", TH), Paragraph("When to Use", TH)],
    [Paragraph("Fully cross-matched", TD), Paragraph("~45 minutes", TD),
     Paragraph("Elective / non-urgent – safest option", TD)],
    [Paragraph("Type-specific (ABO + Rh only)", TD), Paragraph("10–15 minutes", TD),
     Paragraph("Semi-urgent – reduces but doesn't eliminate risk", TD)],
    [Paragraph("O-negative (uncross-matched)", s("on", parent=BOLD, textColor=C_ACCENT)),
     Paragraph("Immediately", s("im", parent=BOLD, textColor=C_ACCENT)),
     Paragraph("Life-threatening emergency. Give O– to females of childbearing age (avoid Rh sensitisation). Give O+ to males.", TD)],
]
story.append(make_table(avail_data, [4.5*cm, 3.5*cm, 9.6*cm]))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 9 – AUTOLOGOUS TRANSFUSION
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑨ AUTOLOGOUS TRANSFUSION", bg=C_GREEN))
story.append(SP(5))

story.append(Paragraph(
    "<b>Definition:</b> You are your own blood donor – your own blood is collected and given back to you.",
    BODY))
story.append(SP(4))

auto_hdr = ["Method", "How It Works", "Advantages", "Disadvantages / Contraindications"]
auto_data = [
    [Paragraph(h, TH) for h in auto_hdr],
    [Paragraph("Pre-donation\n(Pre-operative\nautologous\ndonation)", BOLD),
     Paragraph("Patient donates up to 5 units before elective surgery.\n• First donation: 40 days before surgery\n• Last donation: 3 days before surgery\n• Interval between donations: 3–4 days", TDSMALL),
     Paragraph("Zero immune reactions. No infection risk. No alloimmunisation.", TDSMALL),
     Paragraph("Expensive. Needs planning. Patient must be fit enough to donate.", TDSMALL)],
    [Paragraph("Intra-operative\nCell Salvage", BOLD),
     Paragraph("During surgery:\n1. Suction spilled blood from operative field\n2. Anticoagulate\n3. Filter (remove debris/bone fragments)\n4. Wash with saline\n5. Centrifuge to concentrate RBCs\n6. Re-infuse back into patient", TDSMALL),
     Paragraph("Large-volume recovery during cardiac / orthopaedic / vascular surgery. Very efficient.", TDSMALL),
     Paragraph("Contraindicated in: cancer surgery (may spread tumour cells), infected field, bowel contamination.", TDSMALL)],
]
story.append(make_table(auto_data, [2.5*cm, 5.2*cm, 4.2*cm, 5.7*cm]))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 10 – SHOCK CLASSES
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑩ CLASSES OF HAEMORRHAGIC SHOCK"))
story.append(SP(5))

shock_hdr = ["Class", "Blood Loss (mL)", "% Blood Volume", "Pulse", "BP", "Urine Output", "Mental State", "Fluid Rx"]
shock_data = [
    [Paragraph(h, TH) for h in shock_hdr],
    [Paragraph("I", BOLD), Paragraph("Up to 750", TD), Paragraph("Up to 15%", TD),
     Paragraph("<100", TD), Paragraph("Normal", TD), Paragraph(">30 mL/hr", TD),
     Paragraph("Slightly anxious", TD), Paragraph("Crystalloid", s("c1", parent=TD, textColor=C_GREEN, fontName="Helvetica-Bold"))],
    [Paragraph("II", BOLD), Paragraph("750–1,500", TD), Paragraph("15–30%", TD),
     Paragraph(">100", TD), Paragraph("Normal", TD), Paragraph("20–30 mL/hr", TD),
     Paragraph("Mildly anxious", TD), Paragraph("Crystalloid", s("c2", parent=TD, textColor=C_GREEN, fontName="Helvetica-Bold"))],
    [Paragraph("III", s("iii", parent=BOLD, textColor=C_ORANGE)), Paragraph("1,500–2,000", TD), Paragraph("30–40%", TD),
     Paragraph(">120", TD), Paragraph("Decreased", s("dec", parent=TD, textColor=C_ACCENT, fontName="Helvetica-Bold")),
     Paragraph("5–15 mL/hr", TD), Paragraph("Anxious, confused", TD),
     Paragraph("Crystalloid + Blood", s("cb", parent=TD, textColor=C_ORANGE, fontName="Helvetica-Bold"))],
    [Paragraph("IV", s("iv", parent=BOLD, textColor=C_ACCENT)), Paragraph(">2,000", TD), Paragraph(">40%", TD),
     Paragraph(">140", TD), Paragraph("Decreased", s("dec2", parent=TD, textColor=C_ACCENT, fontName="Helvetica-Bold")),
     Paragraph("Negligible", TD), Paragraph("Confused, lethargic", TD),
     Paragraph("Crystalloid + Blood", s("cb2", parent=TD, textColor=C_ACCENT, fontName="Helvetica-Bold"))],
]
story.append(make_table(shock_data, [1.2*cm, 2.6*cm, 2.6*cm, 1.5*cm, 2*cm, 2.6*cm, 3*cm, 2.1*cm]))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 11 – BLOOD SUBSTITUTES
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑪ BLOOD SUBSTITUTES (Experimental)", bg=C_TEAL))
story.append(SP(5))

sub_hdr = ["Type", "Basis", "Mechanism", "Current Limitation"]
sub_data = [
    [Paragraph(h, TH) for h in sub_hdr],
    [Paragraph("Haemoglobin-based O₂ Carriers (HBOCs)", BOLD),
     Paragraph("Human or bovine Hb, chemically modified", TD),
     Paragraph("Carry O₂ outside red cells", TD),
     Paragraph("Free Hb is toxic – causes vasoconstriction and kidney damage. Failed large clinical trials.", TD)],
    [Paragraph("Perfluorocarbons (PFCs)", BOLD),
     Paragraph("Fully synthetic liquids", TD),
     Paragraph("Dissolve huge amounts of O₂. Must breathe high-concentration O₂.", TD),
     Paragraph("Short half-life. Need high FiO₂. Cardiovascular side effects. Not in routine use.", TD)],
]
story.append(make_table(sub_data, [3.8*cm, 3.5*cm, 4.5*cm, 5.8*cm]))
story.append(SP(3))
story.append(Paragraph(
    "Both types are <b>still experimental</b> – not in routine clinical use, but represent the future of bloodless resuscitation.",
    BODY))
story.append(SP(8))

# ═══════════════════════════════════════════════════════════════
# SECTION 12 – MASTER CHEAT SHEET
# ═══════════════════════════════════════════════════════════════
story.append(section_header("⑫ MASTER CHEAT SHEET – Exam Flash Facts", bg=C_ACCENT))
story.append(SP(5))

cheat_hdr = ["Topic", "Key Fact"]
cheat_data = [
    [Paragraph(h, TH) for h in cheat_hdr],
    [Paragraph("PRBC", BOLD), Paragraph("Hct 50–70%, 330 mL, raises Hb by 1 g/dL, stored 2–6°C up to 6 weeks (SAG-M)", TD)],
    [Paragraph("Platelets", BOLD), Paragraph("250×10⁹/L, 20–24°C on agitator, 5 days shelf life (shortest!), raises count by 5–10k/µL", TD)],
    [Paragraph("FFP", BOLD), Paragraph("All clotting factors, −40°C, 2-year shelf life, use within 20 min of thawing", TD)],
    [Paragraph("Cryoprecipitate", BOLD), Paragraph("Factor VIII + XIII + Fibrinogen + vWF (mnemonic: 8-13-F-vW), −30°C, 2 years", TD)],
    [Paragraph("PCC", BOLD), Paragraph("Factors II, VII, IX, X – best for warfarin reversal (small volume, works in minutes)", TD)],
    [Paragraph("Transfusion trigger", BOLD), Paragraph("Stable patient: <6 g/dL | Symptomatic/active bleed: <8 g/dL | Cardiac: <8–10 g/dL", TD)],
    [Paragraph("Massive transfusion def.", BOLD), Paragraph(">10 units PRBC/24 h OR ≥3 units/1 h with ongoing need OR >150 mL/min blood loss", TD)],
    [Paragraph("Balanced strategy", BOLD), Paragraph("RBC : FFP : Platelets = 1 : 1 : 1 from the very start", TD)],
    [Paragraph("Lethal triad", BOLD), Paragraph("Hypothermia + Acidosis + Coagulopathy (each worsens the others)", TD)],
    [Paragraph("Most common reaction", BOLD), Paragraph("FNHTR (1:1,100) – fever without haemolysis – treat with paracetamol", TD)],
    [Paragraph("AHTR signs (awake)", BOLD), Paragraph("Burning pain in vein, fever, back pain, oliguria, dark urine", TD)],
    [Paragraph("AHTR signs (anaesthetised)", BOLD), Paragraph("Unexplained hypotension + bleeding from wound (DIC)", TD)],
    [Paragraph("TRALI", BOLD), Paragraph("Hypotensive + bilateral infiltrates within 6 h, NO diuretics, 5–10% mortality", TD)],
    [Paragraph("TACO", BOLD), Paragraph("Hypertensive + raised BNP + bilateral oedema, treat with diuretics", TD)],
    [Paragraph("Leukoreduction", BOLD), Paragraph("UK universal policy since 1999; FNHTR ↓ from 30% → <1%; also prevents vCJD transmission", TD)],
    [Paragraph("Universal donor", BOLD), Paragraph("O-negative (give to females of childbearing age in emergency)", TD)],
    [Paragraph("Universal recipient", BOLD), Paragraph("AB-positive (no antibodies)", TD)],
    [Paragraph("Autologous donation", BOLD), Paragraph("Up to 5 units; first 40 days before surgery, last 3 days before; interval 3–4 days", TD)],
    [Paragraph("Tranexamic acid", BOLD), Paragraph("Give early in massive haemorrhage (within 3 h of injury) – antifibrinolytic", TD)],
    [Paragraph("Cell salvage", BOLD), Paragraph("Contraindicated in cancer surgery, infected field, bowel contamination", TD)],
    [Paragraph("Citrate effect", BOLD), Paragraph("Binds Ca²⁺ → hypocalcaemia → tetany, arrhythmia → treat with IV calcium gluconate", TD)],
]
story.append(make_table(cheat_data, [5*cm, 12.6*cm]))
story.append(SP(8))

# ── Sources footer ────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=1, color=C_BORDER))
story.append(SP(3))
story.append(Paragraph(
    "<b>Sources:</b> Goldman-Cecil Medicine | Bailey &amp; Love's Surgery 28th ed. | "
    "Barash Clinical Anesthesia 9th ed. | Henry's Clinical Diagnosis &amp; Laboratory Methods | "
    "Scott-Brown's Surgery | Miller's Anesthesia 10th ed.",
    FOOTER))

# ── Build ─────────────────────────────────────────────────────────
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print("Done:", OUTPUT)
Running Command

cd /home/daytona/workspace/blood-transfusion-qr && python3 make_pdf.py 2>&1

Editing File

~/blood-transfusion-qr/make_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/blood-transfusion-qr/make_pdf.py'
Running Command

grep -n 'parent=BOLD\|parent=TD\|parent=TDSMALL' /home/daytona/workspace/blood-transfusion-qr/make_pdf.py | head -40

Running Command

python3 << 'EOF' import re with open("/home/daytona/workspace/blood-transfusion-qr/make_pdf.py", "r") as f: content = f.read() # Fix the s() helper to accept optional parent kwarg old_s = '''def s(name, **kw): base = styles["Normal"] return ParagraphStyle(name, parent=base, **kw)''' new_s = '''def s(name, **kw): base = kw.pop("parent", styles["Normal"]) return ParagraphStyle(name, parent=base, **kw)''' content = content.replace(old_s, new_s) with open("/home/daytona/workspace/blood-transfusion-qr/make_pdf.py", "w") as f: f.write(content) print("Fixed s() helper") EOF

Running Command

cd /home/daytona/workspace/blood-transfusion-qr && python3 make_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/blood-transfusion-qr/Blood_Transfusion_Quick_Reference.pdf

Your quick reference PDF is ready for download:

Blood Transfusion Quick Reference

PDF Document · PDF


What's inside (12 colour-coded sections):
#SectionWhat's covered
Blood Products at a GlancePRBC, Platelets, FFP, Cryoprecipitate, PCC - storage, shelf life, effect
Storage Solutions (CPDA / SAG-M)Citrate, Phosphate, Dextrose, Adenine, Saline, Mannitol - role & reason
Transfusion TriggersHb thresholds for stable, bleeding, cardiac, septic patients
Massive TransfusionDefinition, Lethal Triad, 1:1:1 strategy, TXA, TEG/ROTEM
ComplicationsHypocalcaemia, hyperkalaemia, hypothermia, coagulopathy, iron overload
Transfusion ReactionsAHTR, FNHTR, Allergic/Anaphylaxis, TRALI, TACO - with TACO vs TRALI table
LeukoreductionWhy, how, UK policy since 1999
ABO Groups & Cross-matchingUniversal donor/recipient, emergency blood availability timelines
Autologous TransfusionPre-donation protocol, intraoperative cell salvage
Haemorrhagic Shock ClassesClass I-IV with fluid management
Blood SubstitutesHBOCs, Perfluorocarbons - why still experimental
Master Cheat Sheet21 rapid-recall exam facts in one table
Sources: Goldman-Cecil Medicine, Bailey & Love 28th ed., Barash Anesthesia 9th ed., Henry's Laboratory Methods, Scott-Brown's Surgery, Miller's Anesthesia 10th ed.

Create a quick reference study pdf for theory exam of this

Running Command

python3 -c " import pdfplumber path = '/home/daytona/workspace/attachments/5ff6b6ba-e96a-485d-add6-00541dc7db24/Here is a much more detailed easy-to-understand ex (1).pdf' with pdfplumber.open(path) as pdf: print(f'Total pages: {len(pdf.pages)}') for i, page in enumerate(pdf.pages): print(f'--- PAGE {i+1} ---') text = page.extract_text() if text: print(text) print() "

Writing File

~/blood-transfusion-qr/make_theory_pdf.py

"""
Blood Transfusion - Theory Exam Quick Reference PDF
Exam-focused: Long answer frameworks, short notes, viva bullets, MCQ traps
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY, TA_RIGHT

OUTPUT = "/home/daytona/workspace/blood-transfusion-qr/Blood_Transfusion_Theory_Exam_StudyGuide.pdf"

W, H = A4  # 595 x 842 pts

# ─── Colour palette ───────────────────────────────────────────────
NAVY     = colors.HexColor("#0d1b4b")
COBALT   = colors.HexColor("#1a3a8c")
SKY      = colors.HexColor("#dce8ff")
RED      = colors.HexColor("#b71c1c")
ROSE     = colors.HexColor("#fde8e8")
GREEN    = colors.HexColor("#1b5e20")
MINT     = colors.HexColor("#e8f5e9")
ORANGE   = colors.HexColor("#bf360c")
PEACH    = colors.HexColor("#fff3e0")
PURPLE   = colors.HexColor("#4a148c")
LAVENDER = colors.HexColor("#ede7f6")
TEAL     = colors.HexColor("#006064")
TEALLT   = colors.HexColor("#e0f7fa")
GOLD     = colors.HexColor("#f57f17")
CREAM    = colors.HexColor("#fffde7")
GREY     = colors.HexColor("#f5f5f5")
LTGREY   = colors.HexColor("#eeeeee")
DARKGREY = colors.HexColor("#424242")
WHITE    = colors.white
BLACK    = colors.black
BORDER   = colors.HexColor("#b0bec5")

# ─── Styles ───────────────────────────────────────────────────────
SS = getSampleStyleSheet()
NRM = SS["Normal"]

def ps(name, **kw):
    parent = kw.pop("parent", NRM)
    return ParagraphStyle(name, parent=parent, **kw)

# Section / display
COVER_TITLE = ps("CT", fontSize=26, textColor=WHITE, fontName="Helvetica-Bold",
                  alignment=TA_CENTER, leading=32, spaceAfter=4)
COVER_SUB   = ps("CS", fontSize=12, textColor=colors.HexColor("#cfd8dc"),
                  fontName="Helvetica", alignment=TA_CENTER, leading=16)
COVER_TAG   = ps("CTG",fontSize=9,  textColor=colors.HexColor("#90caf9"),
                  fontName="Helvetica-Oblique", alignment=TA_CENTER, leading=12)

SEC_HDR     = ps("SH", fontSize=12, textColor=WHITE, fontName="Helvetica-Bold",
                  leading=15, spaceBefore=0, spaceAfter=0)
MINI_HDR    = ps("MH", fontSize=10, textColor=NAVY, fontName="Helvetica-Bold",
                  leading=13, spaceBefore=4, spaceAfter=2)
MINI_HDR_W  = ps("MHW",fontSize=10, textColor=WHITE, fontName="Helvetica-Bold",
                  leading=13)

BODY        = ps("B",  fontSize=8.5, textColor=BLACK, fontName="Helvetica",
                  leading=12, spaceAfter=2)
BODYJ       = ps("BJ", fontSize=8.5, textColor=BLACK, fontName="Helvetica",
                  leading=12, spaceAfter=2, alignment=TA_JUSTIFY)
BOLD        = ps("BO", fontSize=8.5, textColor=BLACK, fontName="Helvetica-Bold",
                  leading=12, spaceAfter=2)
ITALIC      = ps("IT", fontSize=8.5, textColor=DARKGREY, fontName="Helvetica-Oblique",
                  leading=12)
SMALL       = ps("SM", fontSize=7.5, textColor=BLACK, fontName="Helvetica",
                  leading=10)
SMALLB      = ps("SMB",fontSize=7.5, textColor=BLACK, fontName="Helvetica-Bold",
                  leading=10)
RED_BOLD    = ps("RB", fontSize=8.5, textColor=RED,   fontName="Helvetica-Bold",
                  leading=12)
GREEN_BOLD  = ps("GB", fontSize=8.5, textColor=GREEN, fontName="Helvetica-Bold",
                  leading=12)
ORANGE_BOLD = ps("OB", fontSize=8.5, textColor=ORANGE,fontName="Helvetica-Bold",
                  leading=12)
PURPLE_BOLD = ps("PB", fontSize=8.5, textColor=PURPLE,fontName="Helvetica-Bold",
                  leading=12)
TEAL_BOLD   = ps("TB", fontSize=8.5, textColor=TEAL,  fontName="Helvetica-Bold",
                  leading=12)

TH          = ps("TH", fontSize=8,   textColor=WHITE, fontName="Helvetica-Bold",
                  alignment=TA_CENTER, leading=10)
TD          = ps("TD", fontSize=8,   textColor=BLACK, fontName="Helvetica",
                  leading=10)
TDB         = ps("TDB",fontSize=8,   textColor=BLACK, fontName="Helvetica-Bold",
                  leading=10)
TDS         = ps("TDS",fontSize=7.5, textColor=BLACK, fontName="Helvetica",
                  leading=10)
TDSB        = ps("TDSB",fontSize=7.5,textColor=BLACK, fontName="Helvetica-Bold",
                  leading=10)
TDR         = ps("TDR",fontSize=8,   textColor=RED,   fontName="Helvetica-Bold",
                  alignment=TA_CENTER, leading=10)
TDG         = ps("TDG",fontSize=8,   textColor=GREEN, fontName="Helvetica-Bold",
                  alignment=TA_CENTER, leading=10)
TDO         = ps("TDO",fontSize=8,   textColor=ORANGE,fontName="Helvetica-Bold",
                  alignment=TA_CENTER, leading=10)
TDC         = ps("TDC",fontSize=8,   textColor=BLACK, fontName="Helvetica",
                  alignment=TA_CENTER, leading=10)

QST         = ps("QS", fontSize=9,   textColor=NAVY,  fontName="Helvetica-Bold",
                  leading=13, spaceBefore=4, spaceAfter=2)
ANS         = ps("AN", fontSize=8.5, textColor=BLACK, fontName="Helvetica",
                  leading=12, leftIndent=10)
VIVA        = ps("VI", fontSize=8,   textColor=PURPLE,fontName="Helvetica-Bold",
                  leading=11)
VIVAANS     = ps("VA", fontSize=8,   textColor=BLACK, fontName="Helvetica",
                  leading=11, leftIndent=8)
MCQ_Q       = ps("MQ", fontSize=8.5, textColor=NAVY,  fontName="Helvetica-Bold",
                  leading=12, spaceBefore=4)
MCQ_A       = ps("MA", fontSize=8.5, textColor=GREEN, fontName="Helvetica-Bold",
                  leading=12)
MCQ_TRAP    = ps("MT", fontSize=8,   textColor=RED,   fontName="Helvetica-Oblique",
                  leading=11, leftIndent=10)
FOOTER_ST   = ps("FS", fontSize=7,   textColor=colors.grey,
                  alignment=TA_CENTER, leading=9)
WARN        = ps("WN", fontSize=8,   textColor=RED,   fontName="Helvetica-BoldOblique",
                  alignment=TA_CENTER, leading=11)

# ─── Helpers ──────────────────────────────────────────────────────
SP = lambda n=4: Spacer(1, n)

def section_bar(text, bg=NAVY, icon=""):
    full = f"{icon}  {text}" if icon else text
    t = Table([[Paragraph(full, SEC_HDR)]], colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
    ]))
    return t

def colored_box(content_rows, bg=SKY, border_color=COBALT, col_width=None):
    cw = col_width or [W - 3*cm]
    t = Table(content_rows, colWidths=cw)
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("BOX",           (0,0), (-1,-1), 1.2, border_color),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
    ]))
    return t

def sub_header(text, bg=SKY, fg=NAVY):
    sty = ps("_sh", fontSize=9, textColor=fg, fontName="Helvetica-Bold", leading=12)
    t = Table([[Paragraph(text, sty)]], colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("LINEBELOW",     (0,0), (-1,-1), 0.5, border_c(bg)),
    ]))
    return t

def border_c(bg):
    # slightly darker border for sub-headers
    return COBALT

TSTYLE = [
    ("FONTNAME",       (0,0), (-1,0),  "Helvetica-Bold"),
    ("FONTSIZE",       (0,0), (-1,-1), 8),
    ("BACKGROUND",     (0,0), (-1,0),  NAVY),
    ("TEXTCOLOR",      (0,0), (-1,0),  WHITE),
    ("ALIGN",          (0,0), (-1,-1), "CENTER"),
    ("VALIGN",         (0,0), (-1,-1), "MIDDLE"),
    ("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTGREY]),
    ("GRID",           (0,0), (-1,-1), 0.4, BORDER),
    ("TOPPADDING",     (0,0), (-1,-1), 3),
    ("BOTTOMPADDING",  (0,0), (-1,-1), 3),
    ("LEFTPADDING",    (0,0), (-1,-1), 4),
    ("RIGHTPADDING",   (0,0), (-1,-1), 4),
]

def make_table(data, cw, extra=None):
    t = Table(data, colWidths=cw, repeatRows=1)
    ts = list(TSTYLE)
    if extra:
        ts.extend(extra)
    t.setStyle(TableStyle(ts))
    return t

def warn_box(text):
    t = Table([[Paragraph(text, WARN)]], colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), ROSE),
        ("BOX",           (0,0), (-1,-1), 1.5, RED),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ]))
    return t

def tip_box(text):
    sty = ps("_tp", fontSize=8, textColor=GREEN, fontName="Helvetica-BoldOblique",
             alignment=TA_CENTER, leading=11)
    t = Table([[Paragraph(text, sty)]], colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), MINT),
        ("BOX",           (0,0), (-1,-1), 1.5, GREEN),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ]))
    return t

def memory_box(text):
    sty = ps("_mb", fontSize=8.5, textColor=ORANGE, fontName="Helvetica-BoldOblique",
             alignment=TA_CENTER, leading=12)
    t = Table([[Paragraph(text, sty)]], colWidths=[W - 3*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), CREAM),
        ("BOX",           (0,0), (-1,-1), 1.5, GOLD),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ]))
    return t

# ─── Page template ────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(NAVY)
    canvas.rect(0, H - 18, W, 18, fill=1, stroke=0)
    canvas.setFillColor(WHITE)
    canvas.setFont("Helvetica-Bold", 8.5)
    canvas.drawCentredString(W/2, H - 12,
        "BLOOD TRANSFUSION — THEORY EXAM STUDY GUIDE  |  Goldman-Cecil · Bailey & Love · Barash · Henry's · Scott-Brown")
    # accent stripe
    canvas.setFillColor(GOLD)
    canvas.rect(0, H - 20, W, 2, fill=1, stroke=0)
    # footer
    canvas.setFillColor(NAVY)
    canvas.rect(0, 0, W, 14, fill=1, stroke=0)
    canvas.setFillColor(GOLD)
    canvas.rect(0, 14, W, 1.5, fill=1, stroke=0)
    canvas.setFillColor(WHITE)
    canvas.setFont("Helvetica", 7)
    canvas.drawString(1.5*cm, 4, "For educational / exam preparation use only")
    canvas.drawRightString(W - 1.5*cm, 4, f"Page {doc.page}")
    canvas.restoreState()

# ─── Document ─────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT, pagesize=A4,
    leftMargin=1.5*cm, rightMargin=1.5*cm,
    topMargin=1.8*cm, bottomMargin=1.4*cm,
)

story = []

# ══════════════════════════════════════════════════════════════════
# ▌ COVER BLOCK
# ══════════════════════════════════════════════════════════════════
cover = Table(
    [[Paragraph("BLOOD TRANSFUSION", COVER_TITLE)],
     [Paragraph("Theory Exam Study Guide — Easy Language", COVER_SUB)],
     [Paragraph("Long Answers  •  Short Notes  •  Viva Points  •  MCQ Traps  •  Memory Mnemonics", COVER_TAG)],
     [Paragraph("Goldman-Cecil Medicine  •  Bailey &amp; Love 28th  •  Barash Anesthesia 9th  •  Henry's Laboratory Methods  •  Scott-Brown", COVER_TAG)]],
    colWidths=[W - 3*cm]
)
cover.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,-1), NAVY),
    ("TOPPADDING",    (0,0), (-1,-1), 10),
    ("BOTTOMPADDING", (0,0), (-1,-1), 10),
    ("LINEBELOW",     (0,3), (-1,3), 3, GOLD),
]))
story.append(cover)
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# HOW TO USE THIS GUIDE box
# ══════════════════════════════════════════════════════════════════
use_guide = [
    [Paragraph("HOW TO USE THIS GUIDE", ps("_hug", fontSize=9, textColor=TEAL, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=12))],
    [Paragraph(
        "Each section has: <b>(1)</b> Core concept in plain language  "
        "<b>(2)</b> Long-answer writing framework  "
        "<b>(3)</b> Short-note bullet points  "
        "<b>(4)</b> Viva Q&amp;A  "
        "<b>(5)</b> MCQ traps to avoid  "
        "<b>(6)</b> Memory tricks",
        ps("_hb", fontSize=8, textColor=BLACK, fontName="Helvetica", leading=11, alignment=TA_CENTER))],
]
t = Table(use_guide, colWidths=[W - 3*cm])
t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,-1), TEALLT),
    ("BOX",           (0,0), (-1,-1), 1.5, TEAL),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(t)
story.append(SP(10))

# ══════════════════════════════════════════════════════════════════
# SECTION 1: BLOOD PRODUCTS
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 1: BLOOD PRODUCTS — Types, Storage & Uses", NAVY, "①"))
story.append(SP(6))

# ── 1a. Long-answer framework ─────────────────────────────────────
story.append(sub_header("LONG-ANSWER FRAMEWORK — 'Write short notes on blood products used in surgery'"))
story.append(SP(3))

la_intro = [
    [Paragraph("INTRO (2 lines)", TDSB),
     Paragraph("Blood products are derived from whole blood by centrifugation and separation. They allow targeted replacement — giving only what the patient lacks.", TDS)],
    [Paragraph("BODY — list each product with 4 facts:\n(i) composition (ii) storage (iii) shelf life (iv) indication", TDSB),
     Paragraph("Use the table below for the body of your answer.", TDS)],
    [Paragraph("CONCLUSION (2 lines)", TDSB),
     Paragraph("Modern blood banking follows a 'component therapy' philosophy — each component is used rationally. Universal leukoreduction since 1999 has greatly improved safety.", TDS)],
]
t = Table(la_intro, colWidths=[4*cm, 13.6*cm])
t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (0,-1), SKY),
    ("BACKGROUND",    (1,0), (1,-1), WHITE),
    ("ROWBACKGROUNDS",(0,0), (0,-1), [SKY, LTGREY, SKY]),
    ("ROWBACKGROUNDS",(1,0), (1,-1), [WHITE, LTGREY, WHITE]),
    ("GRID",          (0,0), (-1,-1), 0.4, BORDER),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",    (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
]))
story.append(t)
story.append(SP(5))

# ── Products master table ──────────────────────────────────────────
prod_hdr = ["Product", "Composition", "Storage Temp", "Shelf Life", "Main Indication", "Key Exam Fact"]
prod_data = [
    [Paragraph(h, TH) for h in prod_hdr],
    [Paragraph("Whole Blood", TDB),
     Paragraph("RBC + WBC + Plt + ALL plasma proteins + clotting factors", TDS),
     Paragraph("2–6 °C", TDC),
     Paragraph("~3 weeks", TDC),
     Paragraph("Military / trauma (rarely civilian)", TDS),
     Paragraph("'Walking blood bank' — soldiers donate fresh blood on battlefield", TDS)],
    [Paragraph("PRBC\n(Packed Red Cells)", TDB),
     Paragraph("Concentrated RBCs; Hct 50–70% (normal ~45%)", TDS),
     Paragraph("2–6 °C\n(SAG-M additive)", TDC),
     Paragraph("6 weeks", TDC),
     Paragraph("Anaemia — gives only O₂-carrying capacity without extra volume", TDS),
     Paragraph("1 unit = Hb ↑1 g/dL = Hct ↑3%. Volume = 330 mL/unit", TDS)],
    [Paragraph("Platelets", TDB),
     Paragraph("Pooled from 4–6 donors; ~250×10⁹/L", TDS),
     Paragraph("20–24 °C\n(on agitator!)", TDC),
     Paragraph("5 days\n(SHORTEST!)", ps("_s5", fontSize=7.5, textColor=RED, fontName="Helvetica-Bold", leading=10, alignment=TA_CENTER)),
     Paragraph("Thrombocytopenia, platelet dysfunction (aspirin/clopidogrel)", TDS),
     Paragraph("Must NOT be refrigerated — cold activates platelets prematurely", TDS)],
    [Paragraph("FFP\n(Fresh Frozen Plasma)", TDB),
     Paragraph("ALL clotting factors (I,II,V,VII,VIII,IX,X,XI) + fibrinogen", TDS),
     Paragraph("−40 to −50 °C", TDC),
     Paragraph("2 years frozen\nUse within 20 min of thaw!", ps("_ffp", fontSize=7.5, textColor=RED, fontName="Helvetica-Bold", leading=10, alignment=TA_CENTER)),
     Paragraph("Haemorrhage + coagulopathy; warfarin reversal; liver disease; massive transfusion 1:1:1", TDS),
     Paragraph("Rh-negative women receiving large volumes Rh+ FFP → give anti-D", TDS)],
    [Paragraph("Cryoprecipitate", TDB),
     Paragraph("Factor VIII + XIII + Fibrinogen + vWF\n(Mnemonic: 8-13-F-vW)", TDS),
     Paragraph("−30 °C", TDC),
     Paragraph("2 years", TDC),
     Paragraph("Low fibrinogen (first factor to fall in haemorrhage); Haemophilia A; vWD", TDS),
     Paragraph("Give EMPIRICALLY in massive haemorrhage — don't wait for lab results", TDS)],
    [Paragraph("PCC\n(Prothrombin\nComplex\nConcentrate)", TDB),
     Paragraph("Factors II, VII, IX, X\n(vitamin K-dependent)\n4-factor PCC = includes Factor VII", TDS),
     Paragraph("Room temp\n(lyophilised)", TDC),
     Paragraph("Long shelf life", TDC),
     Paragraph("Warfarin reversal (BEST option)", TDS),
     Paragraph("PCC vs FFP: PCC = small vol (~50 mL), works in minutes, purified (lower infection risk). FFP = litres needed, slower.", TDS)],
]
story.append(make_table(prod_data, [2.2*cm, 3.8*cm, 2.2*cm, 2.2*cm, 4*cm, 3.2*cm]))
story.append(SP(5))

story.append(memory_box("MNEMONIC — Cryoprecipitate contents: '8-13-F-vW' = Factor 8 + Factor 13 + Fibrinogen + von Willebrand Factor"))
story.append(SP(4))
story.append(memory_box("WHY platelets at room temp? Cold activates them → they clump prematurely → useless. The agitator prevents settling."))
story.append(SP(6))

# ── Viva Q&A ─────────────────────────────────────────────────────
story.append(sub_header("VIVA QUESTIONS — Blood Products", SKY, NAVY))
story.append(SP(3))
viva1 = [
    ("Q: Which blood product has the shortest shelf life and why?",
     "A: Platelets — only 5 days. Stored at room temp (20–24°C) to prevent activation; longer storage allows bacterial growth and platelet deterioration."),
    ("Q: Why can't SAG-M be used alone as a storage solution?",
     "A: SAG-M has no citrate (anticoagulant). It is an additive — added after plasma is removed from CPD-anticoagulated blood. Together, they give 6-week shelf life."),
    ("Q: When would you choose PCC over FFP for warfarin reversal?",
     "A: Always prefer PCC — it works in minutes, needs only ~50 mL vs litres of FFP, carries lower infection risk (purified), and does not require volume loading."),
    ("Q: What is the significance of '8-13-F-vW' in cryoprecipitate?",
     "A: The mnemonic for cryoprecipitate contents — Factor VIII, Factor XIII, Fibrinogen, von Willebrand Factor. Fibrinogen is most important in massive haemorrhage."),
    ("Q: Why is Rh-positive FFP cautiously given to Rh-negative women?",
     "A: Large volumes of FFP may contain tiny RBC fragments → Rh sensitisation → dangerous in future pregnancies. Anti-D immunoglobulin should be considered."),
]
for q, a in viva1:
    story.append(Paragraph(q, VIVA))
    story.append(Paragraph(a, VIVAANS))
    story.append(SP(2))
story.append(SP(4))

# ── MCQ Traps ────────────────────────────────────────────────────
story.append(sub_header("MCQ TRAPS — Blood Products", ROSE, RED))
story.append(SP(3))
mcq1 = [
    ("FFP must be used within ___ minutes of thawing.", "20 minutes", "Clotting factors degrade rapidly at room temp after thaw."),
    ("Which product is stored at 20–24°C on an agitator?", "Platelets", "PRBC stored at 2–6°C. Platelets at room temp."),
    ("Cryoprecipitate is obtained by slowly thawing FFP at ___.", "4°C", "Thawing at 4°C forms a white precipitate — collected and refrozen."),
    ("PCC contains all EXCEPT which factor?", "Factor V (and sometimes Factor VII in 3-factor PCC)", "4-factor PCC includes II, VII, IX, X. 3-factor PCC omits VII."),
    ("1 unit PRBC raises Hb by ___ g/dL.", "1 g/dL", "Also raises Hct by 3%."),
]
for q, ans, trap in mcq1:
    story.append(Paragraph(f"Q: {q}", MCQ_Q))
    story.append(Paragraph(f"✔ Answer: {ans}", MCQ_A))
    story.append(Paragraph(f"Trap: {trap}", MCQ_TRAP))
    story.append(SP(2))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 2: STORAGE SOLUTIONS
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 2: STORAGE SOLUTIONS — CPDA & SAG-M", COBALT, "②"))
story.append(SP(6))

story.append(sub_header("SHORT-NOTE FRAMEWORK — 'Write a note on anticoagulant-preservative solutions'"))
story.append(SP(3))
story.append(Paragraph(
    "<b>Opening line:</b> Blood stored without additives would clot within minutes and cells would die. "
    "Storage solutions prevent clotting, provide energy, and maintain red cell viability.",
    BODY))
story.append(SP(4))

stor_hdr = ["Component", "Role in Storage", "Mechanism — Why It Matters", "Exam Tip"]
stor_data = [
    [Paragraph(h, TH) for h in stor_hdr],
    [Paragraph("Citrate", TDB),
     Paragraph("Anticoagulant", TDC),
     Paragraph("Chelates (binds) Ca²⁺ ions. Coagulation cascade requires Ca²⁺ at multiple steps — without it, blood cannot clot in the bag.", TDS),
     Paragraph("Citrate from stored blood also binds patient's Ca²⁺ during massive transfusion → hypocalcaemia", TDS)],
    [Paragraph("Phosphate", TDB),
     Paragraph("Buffer + raises 2,3-BPG", TDC),
     Paragraph("Prevents acidosis (pH fall due to cell metabolism). Increases 2,3-BPG in RBCs → lowers Hb-O₂ affinity → better O₂ release to tissues", TDS),
     Paragraph("2,3-BPG shifts oxyhaemoglobin dissociation curve RIGHT (releases O₂ easier)", TDS)],
    [Paragraph("Dextrose\n(glucose)", TDB),
     Paragraph("Energy source", TDC),
     Paragraph("RBCs have no mitochondria → ATP only by anaerobic glycolysis. ATP runs the Na/K pump → maintains RBC shape and prevents haemolysis.", TDS),
     Paragraph("No mitochondria = RBCs ONLY use glucose via glycolysis for energy", TDS)],
    [Paragraph("Adenine", TDB),
     Paragraph("ATP synthesis", TDC),
     Paragraph("Helps regenerate ATP from AMP. Without adenine, ATP depletes and cells die quickly. CPDA (with adenine) lasts 5 weeks; CPD (without) only 2–3 weeks.", TDS),
     Paragraph("CPDA vs CPD: the 'A' = adenine = extra 2 weeks of shelf life", TDS)],
    [Paragraph("Saline\n(SAG-M only)", TDB),
     Paragraph("Diluent", TDC),
     Paragraph("Reduces viscosity of the packed red cell mass so it flows through giving sets and cannulas at adequate speed.", TDS),
     Paragraph("SAG-M = Saline + Adenine + Glucose + Mannitol", TDS)],
    [Paragraph("Mannitol\n(SAG-M only)", TDB),
     Paragraph("Osmotic agent / membrane stabiliser", TDC),
     Paragraph("Prevents haemolysis. Acts as an osmotic membrane stabiliser — maintains the osmotic environment around red cells during storage.", TDS),
     Paragraph("SAG-M cannot be used alone (no citrate). It is an additive to CPD.", TDS)],
]
story.append(make_table(stor_data, [2.5*cm, 2.8*cm, 6.5*cm, 5.8*cm]))
story.append(SP(4))
story.append(memory_box("SAG-M = Saline + Adenine + Glucose + Mannitol  (additive to CPD-blood)  |  CPDA = CPD + Adenine = 5 weeks shelf life"))
story.append(SP(4))

story.append(sub_header("VIVA — Storage Solutions", SKY, NAVY))
story.append(SP(3))
viva2 = [
    ("Q: Why does stored blood cause hypocalcaemia in massively transfused patients?",
     "A: Stored blood contains citrate (anticoagulant). Citrate binds Ca²⁺. In massive transfusion, large citrate load overwhelms the liver (which normally metabolises citrate) → patient's ionised Ca²⁺ falls → tetany, arrhythmia → treat with IV calcium gluconate."),
    ("Q: Why do RBCs in stored blood deplete ATP?",
     "A: RBCs lack mitochondria. They rely solely on anaerobic glycolysis for ATP. Over storage, glucose is consumed and ATP is not adequately regenerated → Na/K pump fails → cells swell and lyse. Adenine in CPDA extends ATP synthesis."),
    ("Q: What shifts the oxygen-haemoglobin dissociation curve in stored blood?",
     "A: Stored blood has reduced 2,3-BPG over time → curve shifts LEFT → Hb holds onto O₂ more tightly → less O₂ released to tissues. Phosphate in storage solutions helps maintain 2,3-BPG."),
]
for q, a in viva2:
    story.append(Paragraph(q, VIVA))
    story.append(Paragraph(a, VIVAANS))
    story.append(SP(2))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 3: TRANSFUSION TRIGGERS
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 3: TRANSFUSION TRIGGERS — Hb Thresholds", RED, "③"))
story.append(SP(6))

story.append(sub_header("CORE CONCEPT + LONG-ANSWER INTRO"))
story.append(SP(3))
story.append(Paragraph(
    "<b>Key principle:</b> Historically, all patients were transfused to Hb > 10 g/dL ('10/30 rule'). "
    "The landmark <b>TRICC trial</b> (Transfusion Requirements in Critical Care) showed that a "
    "<b>restrictive strategy</b> (transfuse at Hb &lt; 7–8) had <b>equal or better outcomes</b> than liberal transfusion. "
    "More blood = more immune reactions + infections + organ failure.",
    BODYJ))
story.append(SP(5))

trig_data = [
    [Paragraph(h, TH) for h in ["Clinical Situation", "Transfuse When Hb <", "Rationale"]],
    [Paragraph("Stable, not bleeding, not going for surgery", TD),
     Paragraph("6 g/dL", ps("_t6", fontSize=10, textColor=GREEN, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=12)),
     Paragraph("Body compensates with ↑cardiac output and ↑O₂ extraction. A resting patient tolerates low Hb well.", TDS)],
    [Paragraph("Actively bleeding / symptomatic / pre-operative", TD),
     Paragraph("8 g/dL", ps("_t8", fontSize=10, textColor=ORANGE, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=12)),
     Paragraph("Increased demand; reduced physiological reserve; needs buffer for ongoing loss or surgical stress.", TDS)],
    [Paragraph("Cardiac ischaemia (ACS, unstable angina, post-MI)", TD),
     Paragraph("8–10 g/dL", ps("_t10", fontSize=10, textColor=RED, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=12)),
     Paragraph("The myocardium is exquisitely sensitive to hypoxia. Ischaemic heart needs maximum O₂ delivery.", TDS)],
    [Paragraph("Septic shock", TD),
     Paragraph("7 g/dL", ps("_t7", fontSize=10, textColor=ORANGE, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=12)),
     Paragraph("TRICC trial data: transfusing at 7 g/dL has same mortality as 9 g/dL threshold. Avoid unnecessary transfusions.", TDS)],
]
story.append(make_table(trig_data, [5*cm, 3*cm, 9.6*cm]))
story.append(SP(5))

story.append(sub_header("VIVA — Transfusion Triggers", SKY, NAVY))
story.append(SP(3))
viva3 = [
    ("Q: What is the TRICC trial and why is it important?",
     "A: TRICC (Transfusion Requirements in Critical Care) trial showed that a restrictive transfusion strategy (trigger at Hb 7 g/dL) had equal or better outcomes vs liberal strategy (trigger at 10 g/dL). It changed global transfusion practice."),
    ("Q: Why is the Hb threshold higher for cardiac patients?",
     "A: Ischaemic myocardium cannot compensate for low O₂ delivery by increasing cardiac output (the heart itself is failing). The myocardium extracts O₂ near-maximally at rest — there is no reserve. Hence higher threshold (8–10 g/dL)."),
    ("Q: What is meant by 'physiological anaemia of surgery'?",
     "A: After elective surgery the body tolerates Hb as low as 6 g/dL if the patient is haemodynamically stable, with no symptoms. This is the basis of the restrictive transfusion threshold."),
]
for q, a in viva3:
    story.append(Paragraph(q, VIVA))
    story.append(Paragraph(a, VIVAANS))
    story.append(SP(2))
story.append(SP(4))

story.append(sub_header("MCQ TRAPS — Transfusion Triggers", ROSE, RED))
story.append(SP(3))
mcq3 = [
    ("Which trial changed transfusion practice from liberal to restrictive?", "TRICC trial", "Not 'CRASH' or 'CRASH-2' — those are for TXA. TRICC = Transfusion Requirements in Critical Care."),
    ("Hb threshold for cardiac ischaemia patient?", "8–10 g/dL (higher range)", "Cardiac patients get a HIGHER threshold because the heart is very sensitive to low O₂."),
    ("The '10/30 rule' refers to?", "Old practice of transfusing to Hb > 10 g/dL and Hct > 30%", "This is now ABANDONED. Use restrictive strategy."),
]
for q, ans, trap in mcq3:
    story.append(Paragraph(f"Q: {q}", MCQ_Q))
    story.append(Paragraph(f"✔ Answer: {ans}", MCQ_A))
    story.append(Paragraph(f"Trap: {trap}", MCQ_TRAP))
    story.append(SP(2))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 4: MASSIVE TRANSFUSION
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 4: MASSIVE TRANSFUSION — Definition, Lethal Triad & Management", GREEN, "④"))
story.append(SP(6))

story.append(sub_header("DEFINITION — Any ONE of the following (Henry's Laboratory Methods)"))
story.append(SP(3))
def_items = [
    "1. Replacement of the entire blood volume (~5 L in a 70 kg adult) within 24 hours",
    "2. Transfusion of > 10 units PRBC within 24 hours",
    "3. Transfusion of ≥ 3 units PRBC in 1 hour when ongoing need is expected",
    "4. Blood loss of > 150 mL/minute (severe ongoing haemorrhage)",
]
for d in def_items:
    story.append(Paragraph(d, BODY))
story.append(SP(6))

# Lethal Triad
story.append(sub_header("THE LETHAL TRIAD — The three-way vicious cycle in massive trauma", bg=ROSE, fg=RED))
story.append(SP(3))

triad_items = [
    ("❄ HYPOTHERMIA", "Cold stored blood (2–6°C) infused rapidly",
     "Coagulation cascade enzymes are temperature-sensitive. At < 35°C they slow dramatically. Platelets also aggregate poorly. Use blood warmers.",
     COBALT, SKY),
    ("⚗ ACIDOSIS", "Poor tissue perfusion → anaerobic metabolism → lactic acid",
     "Acidic pH inhibits clotting enzyme activity (all serine proteases in the cascade function poorly at low pH). Worsens coagulopathy.",
     ORANGE, PEACH),
    ("🩸 COAGULOPATHY", "Dilution from PRBC-only transfusion + crystalloid + consumption in DIC",
     "PRBC contains NO platelets and NO clotting factors. Massive PRBC transfusion dilutes patient's own platelets and factors → dilutional coagulopathy → worsens bleeding.",
     RED, ROSE),
]
for title, cause, effect, fg_c, bg_c in triad_items:
    row = Table([
        [Paragraph(title, ps("_th", fontSize=9, textColor=fg_c, fontName="Helvetica-Bold", leading=12)),
         Paragraph(f"<b>Cause:</b> {cause}  <b>Effect:</b> {effect}", TDS)],
    ], colWidths=[3.5*cm, 14.1*cm])
    row.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (0,0), bg_c),
        ("BACKGROUND",    (1,0), (1,0), WHITE),
        ("BOX",           (0,0), (-1,-1), 0.5, fg_c),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ]))
    story.append(row)
    story.append(SP(2))
story.append(SP(2))
story.append(warn_box("Each element of the triad makes the other two WORSE. This is why a patient can die rapidly even with transfusion if the triad is not ACTIVELY broken."))
story.append(SP(6))

# Management table
story.append(sub_header("MANAGEMENT OF MASSIVE TRANSFUSION — Step by step"))
story.append(SP(3))
mt_hdr = ["Step", "Intervention", "Detail", "Timing / Exam Note"]
mt_data = [
    [Paragraph(h, TH) for h in mt_hdr],
    [Paragraph("1", TDB),
     Paragraph("Balanced 1:1:1\nTransfusion", TDB),
     Paragraph("RBC : FFP : Platelets = 1:1:1 from the very start. Mimics whole blood. Prevents dilutional coagulopathy by replacing all components together.", TDS),
     Paragraph("START EARLY — do not wait for labs to show coagulopathy", TDS)],
    [Paragraph("2", TDB),
     Paragraph("Tranexamic Acid\n(TXA)", TDB),
     Paragraph("Antifibrinolytic — blocks plasmin from dissolving clots. Massive haemorrhage triggers hyperfibrinolysis (clot-dissolving system goes into overdrive). TXA stops this.", TDS),
     Paragraph("WITHIN 3 HOURS of injury. After 3 h it can paradoxically increase mortality (CRASH-2 trial)", TDS)],
    [Paragraph("3", TDB),
     Paragraph("Cryoprecipitate\n(empirically)", TDB),
     Paragraph("Fibrinogen is the FIRST clotting factor to critically fall in haemorrhage. Clots cannot form without fibrinogen. Give cryoprecipitate empirically — do NOT wait for lab results.", TDS),
     Paragraph("Fibrinogen target: > 1.5–2 g/L. If Fib < 1.5 → give cryo NOW", TDS)],
    [Paragraph("4", TDB),
     Paragraph("IV Calcium Gluconate", TDB),
     Paragraph("Citrate in stored blood binds ionised Ca²⁺ → hypocalcaemia → worsens coagulopathy and causes cardiac arrhythmia. Give IV calcium gluconate proactively in massive transfusion.", TDS),
     Paragraph("Check ionised Ca²⁺ regularly. Also treat arrhythmia", TDS)],
    [Paragraph("5", TDB),
     Paragraph("Blood Warmers", TDB),
     Paragraph("Cold stored blood causes hypothermia → clotting enzymes stop working. Use in-line blood warmers for ALL products during massive transfusion.", TDS),
     Paragraph("Aim core temp > 35°C for functional coagulation", TDS)],
    [Paragraph("6", TDB),
     Paragraph("TEG / ROTEM\n(Point-of-care)", TDB),
     Paragraph("Thromboelastography/Thromboelastometry — gives a real-time graph of clot formation, strength, and lysis in minutes. Guides targeted replacement (which factor is deficient).", TDS),
     Paragraph("Much faster than sending blood to central lab. Guides rational treatment.", TDS)],
    [Paragraph("7", TDB),
     Paragraph("Platelets if\n< 50,000/µL", TDB),
     Paragraph("Monitor platelet count. Transfuse platelet concentrate if count falls below 50,000/µL in an actively bleeding patient.", TDS),
     Paragraph("Platelet transfusion threshold in surgery: < 50,000/µL (< 100,000 for brain/eye surgery)", TDS)],
]
story.append(make_table(mt_data, [1*cm, 3.2*cm, 8.4*cm, 5*cm]))
story.append(SP(5))

story.append(sub_header("VIVA — Massive Transfusion", SKY, NAVY))
story.append(SP(3))
viva4 = [
    ("Q: What is the lethal triad and why is each component dangerous?",
     "A: Hypothermia + Acidosis + Coagulopathy. (1) Hypothermia slows clotting enzymes. (2) Acidosis inhibits serine proteases in the coagulation cascade. (3) Coagulopathy (dilutional) prevents effective clot formation. Each element worsens the others in a vicious cycle."),
    ("Q: Why is tranexamic acid given within 3 hours and not later?",
     "A: CRASH-2 trial showed TXA given within 1 hour reduces mortality most. Given 1–3 hours: still beneficial. Given AFTER 3 hours: paradoxically increases mortality, possibly by allowing microthrombi to form in compromised vessels."),
    ("Q: Why is fibrinogen described as the 'most critical' factor in massive haemorrhage?",
     "A: Fibrinogen (Factor I) is present at the lowest molar concentration of all clotting factors, and it is consumed most rapidly in coagulopathic bleeding. Without fibrinogen, the final step of clot formation (fibrinogen → fibrin polymer) cannot occur — no clot forms at all, regardless of other factors."),
    ("Q: Explain dilutional coagulopathy step by step.",
     "A: Step 1 — Massive bleeding: PRBC + crystalloid given. Step 2 — PRBC has NO platelets, NO clotting factors. Crystalloid dilutes further. Step 3 — Patient's own platelets and factors now critically diluted. Step 4 — Clots cannot form. Step 5 — More bleeding. Vicious cycle = dilutional coagulopathy."),
]
for q, a in viva4:
    story.append(Paragraph(q, VIVA))
    story.append(Paragraph(a, VIVAANS))
    story.append(SP(2))
story.append(SP(4))

story.append(sub_header("MCQ TRAPS — Massive Transfusion", ROSE, RED))
story.append(SP(3))
mcq4 = [
    ("TXA should be given within ___ hours of injury.", "3 hours", "After 3 hours it can INCREASE mortality. This is the CRASH-2 trial finding."),
    ("The 1:1:1 ratio means RBC:FFP:Platelets. What does this mimic?", "Whole blood composition", "The rationale is to replace ALL components together to prevent dilutional coagulopathy."),
    ("Which clotting factor falls FIRST and FASTEST in massive haemorrhage?", "Fibrinogen (Factor I)", "Not Factor VIII (that's haemophilia). Fibrinogen is the first to critically fall."),
    ("TEG/ROTEM is used for?", "Point-of-care monitoring of the entire coagulation process", "Not for platelet counting. It gives a real-time graph of clot formation and lysis."),
]
for q, ans, trap in mcq4:
    story.append(Paragraph(f"Q: {q}", MCQ_Q))
    story.append(Paragraph(f"✔ Answer: {ans}", MCQ_A))
    story.append(Paragraph(f"Trap: {trap}", MCQ_TRAP))
    story.append(SP(2))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 5: TRANSFUSION REACTIONS
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 5: TRANSFUSION REACTIONS — Identification & Management", RED, "⑤"))
story.append(SP(6))

story.append(sub_header("LONG-ANSWER FRAMEWORK — 'Classify and discuss transfusion reactions'"))
story.append(SP(3))
story.append(Paragraph(
    "<b>Classification:</b> (A) Immunological — AHTR, FNHTR, Allergic/Anaphylaxis, TRALI  "
    "(B) Non-immunological — TACO, sepsis, iron overload, metabolic (hypocalcaemia, hyperkalaemia)  "
    "(C) Delayed — delayed haemolytic, alloimmunisation, GVHD, post-transfusion purpura",
    BODY))
story.append(SP(5))

rx_hdr = ["Reaction", "Frequency", "Mechanism (for exams)", "Presentation", "Management"]
rx_data = [
    [Paragraph(h, TH) for h in rx_hdr],
    [Paragraph("Acute Haemolytic\nTransfusion Reaction\n(AHTR)", ps("_ah", fontSize=8, textColor=RED, fontName="Helvetica-Bold", leading=10)),
     Paragraph("1:110,000\n(rare, FATAL)", TDR),
     Paragraph("ABO mismatch → pre-formed IgM antibodies → complement cascade → intravascular haemolysis → free Hb → AKI + DIC + shock", TDS),
     Paragraph("Awake: Burning pain in vein (1st sign!), fever, back/flank pain, hypotension, oliguria, dark red urine\nAnaesthetised: Unexplained hypotension + bleeding (DIC)", TDS),
     Paragraph("1. STOP transfusion IMMEDIATELY\n2. IV saline (keep line open)\n3. Recheck blood bank\n4. IV fluids + diuretics (furosemide/mannitol)\n5. Treat DIC + hypotension\n6. Monitor urine output hourly", TDS)],
    [Paragraph("Febrile Non-\nHaemolytic (FNHTR)\n★ Most Common", ps("_fn", fontSize=8, textColor=COBALT, fontName="Helvetica-Bold", leading=10)),
     Paragraph("1:1,100\n(MOST COMMON)", TDG),
     Paragraph("(1) Recipient antibodies (from prior transfusion/pregnancy) attack donor WBCs (HLA antigens) → cytokines → fever\n(2) Pre-formed cytokines in platelet storage → fever on transfusion", TDS),
     Paragraph("Fever ≥1°C rise (or >38°C) within 4 h of transfusion\nChills, rigors, nausea, headache\nNO haemolysis, NO hypotension", TDS),
     Paragraph("Stop transfusion. Paracetamol.\nRule out AHTR first!\nPrevention: Leukoreduction (FNHTR ↓ from 30% → <1%)", TDS)],
    [Paragraph("Allergic /\nAnaphylaxis", ps("_al", fontSize=8, textColor=ORANGE, fontName="Helvetica-Bold", leading=10)),
     Paragraph("1:1,200\n(urticaria common;\nanaphylaxis rare)", TDO),
     Paragraph("IgE-mediated reaction to donor plasma proteins.\nSpecial: IgA-deficient patients who have anti-IgA antibodies → anaphylaxis with any IgA-containing product", TDS),
     Paragraph("Mild: Urticaria, itching, flushing\nModerate: Angioedema, wheeze\nSevere: Bronchospasm, hypotension, collapse (anaphylaxis)", TDS),
     Paragraph("Mild: Antihistamines; may continue at slower rate\nSevere: STOP + Adrenaline 1:1000 IM\nIgA deficient: IgA-deficient blood or washed products", TDS)],
    [Paragraph("TRALI\n(Transfusion-related\nAcute Lung Injury)", ps("_tr", fontSize=8, textColor=PURPLE, fontName="Helvetica-Bold", leading=10)),
     Paragraph("1:140,000\n(was leading cause\nof death)", ps("_trp", fontSize=7.5, textColor=PURPLE, fontName="Helvetica-Bold", leading=10, alignment=TA_CENTER)),
     Paragraph("'Two-hit model': (1) Patient's lung neutrophils primed by illness. (2) Donor anti-HLA antibodies (esp. multiparous women) activate primed neutrophils → massive inflammation → capillary leak → non-cardiogenic oedema", TDS),
     Paragraph("Within 6 h: sudden hypoxia + HYPOTENSION (key!) + bilateral infiltrates on CXR (snowstorm) + NO heart failure signs", TDS),
     Paragraph("STOP transfusion. High-flow O₂ ± mechanical ventilation.\nNO diuretics (not fluid overload — capillary leak).\nSupportive only. Recover in 2–4 days. 5–10% mortality.\nPrevention: male donor plasma / antibody-tested donors", TDS)],
    [Paragraph("TACO\n(Transfusion-\nAssociated\nCirculatory\nOverload)", ps("_tc", fontSize=8, textColor=TEAL, fontName="Helvetica-Bold", leading=10)),
     Paragraph("1:9,000\n(more common\nthan TRALI)", ps("_tcp", fontSize=7.5, textColor=TEAL, fontName="Helvetica-Bold", leading=10, alignment=TA_CENTER)),
     Paragraph("Simple fluid overload. Elderly / heart failure / renal failure patients cannot handle extra volume → cardiogenic pulmonary oedema (hydrostatic, not capillary leak)", TDS),
     Paragraph("Within 2–6 h (up to 12 h): dyspnoea, HYPERTENSION (key!), tachycardia, raised JVP, orthopnoea, bilateral oedema, raised BNP > 1000 pg/mL", TDS),
     Paragraph("SLOW/STOP transfusion.\nFurosemide (diuretics — UNLIKE TRALI).\nSit upright. O₂.\nBNP elevated — differentiates from TRALI", TDS)],
]
story.append(make_table(rx_data, [2.6*cm, 2.2*cm, 4.2*cm, 4.2*cm, 4.4*cm]))
story.append(SP(6))

# TACO vs TRALI comparison
story.append(sub_header("TACO vs TRALI — The Most Exam-Tested Comparison"))
story.append(SP(3))
comp_hdr = ["Feature", "TACO", "TRALI"]
comp_data = [
    [Paragraph(h, TH) for h in comp_hdr],
    [Paragraph("Full name", TDB), Paragraph("Transfusion-Associated Circulatory Overload", TDS), Paragraph("Transfusion-Related Acute Lung Injury", TDS)],
    [Paragraph("Mechanism", TDB), Paragraph("Fluid overload → cardiogenic oedema", TDS), Paragraph("Immune-mediated → non-cardiogenic (capillary leak) oedema", TDS)],
    [Paragraph("Blood pressure", TDB),
     Paragraph("⬆ HYPERTENSIVE", ps("_tah", fontSize=8, textColor=RED, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10)),
     Paragraph("⬇ HYPOTENSIVE", ps("_trh", fontSize=8, textColor=GREEN, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10))],
    [Paragraph("Onset", TDB), Paragraph("2–6 h (up to 12 h)", TDC), Paragraph("Within 6 h", TDC)],
    [Paragraph("BNP level", TDB),
     Paragraph("ELEVATED (> 1000 pg/mL)", ps("_bte", fontSize=8, textColor=RED, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10)),
     Paragraph("Normal / mildly elevated", TDC)],
    [Paragraph("CXR", TDB),
     Paragraph("Cardiomegaly + bilateral oedema + pleural effusions", TDC),
     Paragraph("Bilateral infiltrates ONLY — no cardiomegaly", TDC)],
    [Paragraph("Diuretics?", TDB),
     Paragraph("YES — furosemide fixes it", ps("_tdy", fontSize=8, textColor=GREEN, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10)),
     Paragraph("NO diuretics — may worsen hypotension!", ps("_trdn", fontSize=8, textColor=RED, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10))],
    [Paragraph("Risk factors", TDB), Paragraph("Elderly, heart failure, renal failure, large volume rapid transfusion", TDS), Paragraph("Any patient; multiparous donor plasma; ICU patients", TDS)],
    [Paragraph("Mortality", TDB), Paragraph("Good prognosis with treatment", TDG), Paragraph("5–10% mortality", TDR)],
]
story.append(make_table(comp_data, [3.2*cm, 8.3*cm, 6.1*cm]))
story.append(SP(4))
story.append(memory_box(
    "TACO = Too much fluid = Hypertension = BNP high = Diuretics WORK  |  "
    "TRALI = Two-hit immune attack = Hypotension = BNP normal = NO diuretics"))
story.append(SP(6))

story.append(sub_header("VIVA — Transfusion Reactions", SKY, NAVY))
story.append(SP(3))
viva5 = [
    ("Q: What is the MOST COMMON transfusion reaction and what has dramatically reduced its incidence?",
     "A: Febrile Non-Haemolytic Transfusion Reaction (FNHTR) — occurs in 1:1,100 transfusions. Universal leukoreduction (removing WBCs) in the UK since 1999 reduced incidence from ~30% to < 1%."),
    ("Q: Why is AHTR so dangerous in an anaesthetised patient?",
     "A: The patient cannot report the earliest symptom (burning pain along the vein). The only signs are unexplained hypotension and bleeding from the wound (due to DIC consuming all clotting factors). The reaction can go undetected until cardiovascular collapse."),
    ("Q: Explain the two-hit model of TRALI.",
     "A: Hit 1 — Patient's lungs are 'primed' by illness (surgery/trauma/infection). Neutrophils accumulate in pulmonary capillaries, slightly activated. Hit 2 — Donor blood (especially from multiparous women) contains anti-HLA antibodies. These activate the primed neutrophils → massive inflammatory response → capillary leak → non-cardiogenic pulmonary oedema (TRALI)."),
    ("Q: Who is at risk for anaphylaxis during transfusion and why?",
     "A: Patients with IgA deficiency who have developed anti-IgA antibodies. When transfused with IgA-containing blood products (plasma, FFP), anti-IgA binds donor IgA → anaphylaxis. These patients must receive IgA-deficient or washed blood products."),
]
for q, a in viva5:
    story.append(Paragraph(q, VIVA))
    story.append(Paragraph(a, VIVAANS))
    story.append(SP(2))
story.append(SP(4))

story.append(sub_header("MCQ TRAPS — Transfusion Reactions", ROSE, RED))
story.append(SP(3))
mcq5 = [
    ("The first symptom of AHTR in an AWAKE patient is?", "Burning pain along the vein", "NOT fever — burning pain is the EARLIEST sign. Fever comes later."),
    ("In an ANAESTHETISED patient with AHTR, the clues are?", "Unexplained hypotension + bleeding from wound (DIC)", "Patient cannot report pain. Anaesthetist must be vigilant for these two signs."),
    ("TRALI is treated with diuretics: True or False?", "FALSE — No diuretics in TRALI", "TACO → diuretics YES. TRALI → diuretics NO (oedema is capillary leak, not fluid overload)."),
    ("FNHTR occurs within ___ hours of transfusion.", "Within 4 hours", "Defined as temp rise ≥ 1°C within 4 hours of transfusion — with NO haemolysis."),
    ("Which type of donor is most associated with TRALI?", "Multiparous women (developed anti-HLA antibodies from pregnancies)", "Male donors or antibody-screened donors are now preferred for plasma products to prevent TRALI."),
]
for q, ans, trap in mcq5:
    story.append(Paragraph(f"Q: {q}", MCQ_Q))
    story.append(Paragraph(f"✔ Answer: {ans}", MCQ_A))
    story.append(Paragraph(f"Trap: {trap}", MCQ_TRAP))
    story.append(SP(2))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 6: LEUKOREDUCTION
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 6: LEUKOREDUCTION", TEAL, "⑥"))
story.append(SP(6))

story.append(Paragraph(
    "<b>Definition:</b> Leukoreduction (leukodepletion) = filtration of WBCs from blood products before transfusion. "
    "UK universal policy since 1999.",
    BODY))
story.append(SP(4))

leuko_data = [
    [Paragraph(h, TH) for h in ["Reason", "Mechanism", "Outcome"]],
    [Paragraph("Prevent FNHTR\n(#1 reason)", TDB),
     Paragraph("Recipient antibodies attack donor WBCs → cytokines → fever. Removing WBCs eliminates the target.", TDS),
     Paragraph("FNHTR incidence ↓ from 30% → 0.01–1% after universal leukodepletion", TDS)],
    [Paragraph("Prevent vCJD\ntransmission", TDB),
     Paragraph("Prion proteins (variant Creutzfeldt-Jakob / 'mad cow disease') may be carried on WBCs. Removing WBCs reduces theoretical transmission risk.", TDS),
     Paragraph("UK introduced universal leukodepletion in 1999 partly in response to vCJD epidemic", TDS)],
    [Paragraph("Reduce alloimmunisation\n/ immunogenicity", TDB),
     Paragraph("Donor WBCs express foreign HLA antigens → prime recipient's immune system → antibodies formed → problems in future transfusions (harder to cross-match)", TDS),
     Paragraph("Reduces HLA alloimmunisation; important in patients needing repeated transfusions", TDS)],
]
story.append(make_table(leuko_data, [3.5*cm, 7.5*cm, 6.6*cm]))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 7: ABO BLOOD GROUPS & CROSS-MATCHING
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 7: BLOOD GROUPS & CROSS-MATCHING", COBALT, "⑦"))
story.append(SP(6))

abo_data = [
    [Paragraph(h, TH) for h in ["Blood Group", "Antigen on RBC", "Antibody in Plasma", "Exam Role"]],
    [Paragraph("A", TDB), Paragraph("A antigen", TDC), Paragraph("Anti-B", TDC), Paragraph("—", TDC)],
    [Paragraph("B", TDB), Paragraph("B antigen", TDC), Paragraph("Anti-A", TDC), Paragraph("—", TDC)],
    [Paragraph("AB", ps("_ab", fontSize=8, textColor=GREEN, fontName="Helvetica-Bold", leading=10)),
     Paragraph("A and B antigens", TDC), Paragraph("NONE", TDC),
     Paragraph("Universal RECIPIENT — no antibodies to attack any donor RBC", TDG)],
    [Paragraph("O", ps("_o", fontSize=8, textColor=RED, fontName="Helvetica-Bold", leading=10)),
     Paragraph("NONE", TDC), Paragraph("Anti-A AND Anti-B", TDC),
     Paragraph("Universal DONOR — no antigens for recipient antibodies to attack", TDR)],
]
story.append(make_table(abo_data, [3*cm, 4*cm, 4.5*cm, 6.1*cm]))
story.append(SP(4))

avail_data = [
    [Paragraph(h, TH) for h in ["Blood Type Available", "Time", "Use In"]],
    [Paragraph("Fully cross-matched (patient serum + donor RBC → check agglutination)", TDS),
     Paragraph("~45 min", TDC), Paragraph("Elective / non-urgent cases", TDS)],
    [Paragraph("Type-specific (ABO + Rh only matched)", TDS),
     Paragraph("10–15 min", TDC), Paragraph("Semi-urgent — balance risk vs time", TDS)],
    [Paragraph("O-negative (uncross-matched) — Universal donor", ps("_on", fontSize=8, textColor=RED, fontName="Helvetica-Bold", leading=10)),
     Paragraph("IMMEDIATE", TDR),
     Paragraph("Life-threatening emergency.\nO– for females of childbearing age (prevent Rh sensitisation)\nO+ for males (Rh less critical)", TDS)],
]
story.append(make_table(avail_data, [7.5*cm, 2.5*cm, 7.6*cm]))
story.append(SP(4))
story.append(memory_box("Emergency blood priority: O-negative to females of childbearing age | O-positive to males | To buy time until cross-match is ready"))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 8: AUTOLOGOUS TRANSFUSION
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 8: AUTOLOGOUS TRANSFUSION", GREEN, "⑧"))
story.append(SP(6))

story.append(Paragraph(
    "<b>Definition:</b> 'Autologous' = self. Patient is their own blood donor. Blood collected from the patient and given back to the same patient.",
    BODY))
story.append(SP(4))

auto_data = [
    [Paragraph(h, TH) for h in ["Method", "Protocol / Steps", "Advantages", "Contraindications"]],
    [Paragraph("Pre-operative\nAutologous\nDonation (PAD)", TDB),
     Paragraph("• Up to 5 units donated before elective surgery\n• First donation: 40 days before surgery (body needs time to recover)\n• Last donation: 3 days before surgery (fresh)\n• Interval between donations: 3–4 days", TDS),
     Paragraph("Zero immune reactions. No infection transmission. No alloimmunisation. No cross-matching needed.", TDS),
     Paragraph("Patient must be fit to donate. Expensive. Requires planning weeks ahead.", TDS)],
    [Paragraph("Intra-operative\nCell Salvage\n(ICS)", TDB),
     Paragraph("1. Suction blood from operative field\n2. Anticoagulate (heparin)\n3. Filter (remove bone chips, fat, debris)\n4. Wash with saline\n5. Centrifuge to concentrate RBCs\n6. Re-infuse into patient", TDS),
     Paragraph("Recovers large volumes during cardiac / orthopaedic / vascular surgery. Very efficient in high-blood-loss cases.", TDS),
     Paragraph("CONTRAINDICATED in:\n• Cancer surgery (risk of reinfusing tumour cells)\n• Infected surgical field (risk of bacteraemia)\n• Bowel contamination", TDS)],
]
story.append(make_table(auto_data, [2.8*cm, 5.5*cm, 4.2*cm, 5.1*cm]))
story.append(SP(6))

story.append(sub_header("VIVA — Autologous Transfusion", SKY, NAVY))
story.append(SP(3))
viva8 = [
    ("Q: What is the protocol for pre-operative autologous donation?",
     "A: Up to 5 units can be pre-donated. First donation 40 days before surgery (to allow recovery). Last donation 3 days before surgery (still fresh). Interval 3–4 days between donations."),
    ("Q: Why is cell salvage contraindicated in cancer surgery?",
     "A: Suctioned blood from the operative field may contain shed tumour cells. Re-infusing this blood could cause haematogenous spread (metastasis) of the cancer."),
]
for q, a in viva8:
    story.append(Paragraph(q, VIVA))
    story.append(Paragraph(a, VIVAANS))
    story.append(SP(2))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 9: HAEMORRHAGIC SHOCK CLASSES
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 9: CLASSES OF HAEMORRHAGIC SHOCK", ORANGE, "⑨"))
story.append(SP(6))

shock_hdr = ["Class", "Blood Loss\n(mL)", "% Blood\nVolume", "Pulse\n(bpm)", "BP", "Urine Output", "Mental State", "Fluid Management"]
shock_data = [
    [Paragraph(h, TH) for h in shock_hdr],
    [Paragraph("I", TDB), Paragraph("< 750", TDC), Paragraph("< 15%", TDC),
     Paragraph("< 100", TDC), Paragraph("Normal", TDG), Paragraph("> 30 mL/hr", TDC),
     Paragraph("Slightly\nanxious", TDC),
     Paragraph("Crystalloid only", ps("_ci", fontSize=7.5, textColor=GREEN, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10))],
    [Paragraph("II", TDB), Paragraph("750–1500", TDC), Paragraph("15–30%", TDC),
     Paragraph("> 100", TDC), Paragraph("Normal", TDG), Paragraph("20–30 mL/hr", TDC),
     Paragraph("Mildly\nanxious", TDC),
     Paragraph("Crystalloid only", ps("_cii", fontSize=7.5, textColor=GREEN, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10))],
    [Paragraph("III", ps("_iiib", fontSize=8, textColor=ORANGE, fontName="Helvetica-Bold", leading=10)),
     Paragraph("1500–2000", TDC), Paragraph("30–40%", TDC),
     Paragraph("> 120", TDO), Paragraph("Decreased", TDO), Paragraph("5–15 mL/hr", TDC),
     Paragraph("Anxious,\nconfused", TDC),
     Paragraph("Crystalloid + Blood", ps("_ciii", fontSize=7.5, textColor=ORANGE, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10))],
    [Paragraph("IV", ps("_ivb", fontSize=8, textColor=RED, fontName="Helvetica-Bold", leading=10)),
     Paragraph("> 2000", TDC), Paragraph("> 40%", TDC),
     Paragraph("> 140", TDR), Paragraph("Decreased", TDR), Paragraph("Negligible", TDR),
     Paragraph("Confused,\nlethargic", TDC),
     Paragraph("Crystalloid + Blood\n(URGENT)", ps("_civ", fontSize=7.5, textColor=RED, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10))],
]
story.append(make_table(shock_data, [1.2*cm, 2.2*cm, 2*cm, 1.8*cm, 2.2*cm, 2.5*cm, 2.2*cm, 3.5*cm]))
story.append(SP(4))
story.append(tip_box("Class I + II = Crystalloid alone   |   Class III + IV = Crystalloid + Blood products (consider massive transfusion protocol)"))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 10: BLOOD SUBSTITUTES
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 10: BLOOD SUBSTITUTES (Experimental)", TEAL, "⑩"))
story.append(SP(6))

story.append(Paragraph(
    "<b>Why needed?</b> Real blood has limited supply, carries infection risk, has short shelf life, and needs complex storage and matching.",
    BODY))
story.append(SP(4))

sub_data = [
    [Paragraph(h, TH) for h in ["Type", "Basis", "How It Works", "Problems / Exam Note"]],
    [Paragraph("Haemoglobin-based\nO₂ Carriers\n(HBOCs)", TDB),
     Paragraph("Human or bovine (cow) haemoglobin, chemically modified", TDS),
     Paragraph("Carry O₂ without needing to be inside RBCs. The modified Hb molecule dissolves in plasma.", TDS),
     Paragraph("Free Hb in plasma is TOXIC → causes vasoconstriction (scavenges NO) and kidney damage. Failed multiple large clinical trials. Not in routine use.", TDS)],
    [Paragraph("Perfluorocarbons\n(PFCs)", TDB),
     Paragraph("Fully synthetic (no biological origin)", TDS),
     Paragraph("Perfluorocarbon liquids dissolve O₂ and CO₂ in huge amounts. Must breathe high-concentration O₂ (high FiO₂) for them to load O₂.", TDS),
     Paragraph("Short half-life. Require high FiO₂ to work. Cardiovascular side effects. Still experimental — not routine.", TDS)],
]
story.append(make_table(sub_data, [3*cm, 3.5*cm, 5.5*cm, 5.6*cm]))
story.append(SP(4))
story.append(warn_box("Both HBOCs and PFCs are EXPERIMENTAL — not in routine clinical use. Mentioning this in exams is important."))
story.append(SP(8))

# ══════════════════════════════════════════════════════════════════
# SECTION 11: EXAM MASTER SUMMARY TABLE
# ══════════════════════════════════════════════════════════════════
story.append(section_bar("SECTION 11: RAPID REVISION — One-Line Exam Facts", RED, "★"))
story.append(SP(5))

master_hdr = ["Topic", "The Exam Answer (one line)"]
master_data = [
    [Paragraph(h, TH) for h in master_hdr],
    [Paragraph("PRBC details", TDB), Paragraph("Hct 50–70%, volume 330 mL, 2–6°C in SAG-M, shelf life 6 weeks, raises Hb by 1 g/dL per unit", TDS)],
    [Paragraph("Platelets — WHY room temp?", TDB), Paragraph("Cold activates platelets → premature clumping → they stop working. Agitator prevents settling.", TDS)],
    [Paragraph("Platelets shelf life", TDB), Paragraph("5 days — shortest of all blood products", TDS)],
    [Paragraph("FFP — post-thaw use", TDB), Paragraph("Use within 20 minutes of thawing — clotting factors degrade rapidly at room temperature", TDS)],
    [Paragraph("Cryoprecipitate mnemonic", TDB), Paragraph("8-13-F-vW = Factor VIII, Factor XIII, Fibrinogen, von Willebrand Factor", TDS)],
    [Paragraph("PCC vs FFP for warfarin", TDB), Paragraph("PCC wins — small volume (~50 mL), works in minutes, purified (lower infection risk)", TDS)],
    [Paragraph("SAG-M", TDB), Paragraph("Saline + Adenine + Glucose + Mannitol. Additive to CPD blood. Cannot be used alone (no citrate).", TDS)],
    [Paragraph("CPDA vs CPD shelf life", TDB), Paragraph("CPDA (with adenine) = 5 weeks. CPD (without adenine) = 2–3 weeks only.", TDS)],
    [Paragraph("Transfusion trigger — stable", TDB), Paragraph("Hb < 6 g/dL (TRICC trial)", TDS)],
    [Paragraph("Transfusion trigger — cardiac", TDB), Paragraph("Hb < 8–10 g/dL (heart is most sensitive to low O₂)", TDS)],
    [Paragraph("Massive transfusion definition", TDB), Paragraph("> 10 units PRBC/24 h OR ≥ 3 units/1 h with ongoing need OR > 150 mL/min blood loss", TDS)],
    [Paragraph("Balanced strategy", TDB), Paragraph("RBC : FFP : Platelets = 1 : 1 : 1 from the START (mimics whole blood)", TDS)],
    [Paragraph("Lethal triad", TDB), Paragraph("Hypothermia + Acidosis + Coagulopathy — each worsens the others", TDS)],
    [Paragraph("Tranexamic acid timing", TDB), Paragraph("Give within 3 hours of injury. After 3 hours it can paradoxically WORSEN mortality (CRASH-2 trial)", TDS)],
    [Paragraph("First clotting factor to fall in haemorrhage", TDB), Paragraph("Fibrinogen (Factor I) — give cryoprecipitate empirically without waiting for labs", TDS)],
    [Paragraph("Most common transfusion reaction", TDB), Paragraph("FNHTR (1:1,100). Treated with paracetamol. Prevented by leukoreduction.", TDS)],
    [Paragraph("AHTR first sign (awake patient)", TDB), Paragraph("Burning pain along the vein — the patient feels it immediately", TDS)],
    [Paragraph("AHTR in anaesthetised patient", TDB), Paragraph("Unexplained hypotension + bleeding from wound (DIC consuming clotting factors)", TDS)],
    [Paragraph("TRALI vs TACO BP", TDB), Paragraph("TRALI = HYPOTENSIVE. TACO = HYPERTENSIVE. KEY differentiator.", TDS)],
    [Paragraph("TRALI treatment", TDB), Paragraph("Stop transfusion + high-flow O₂ ± ventilator. NO diuretics. Supportive only.", TDS)],
    [Paragraph("TACO treatment", TDB), Paragraph("Furosemide (diuretics) + sit upright + O₂. BNP elevated.", TDS)],
    [Paragraph("Leukoreduction — UK policy", TDB), Paragraph("Universal for ALL blood components since 1999. FNHTR ↓ from 30% → < 1%. Also prevents vCJD.", TDS)],
    [Paragraph("Universal donor", TDB), Paragraph("O-negative (no antigens). Give to females of childbearing age in emergency to prevent Rh sensitisation.", TDS)],
    [Paragraph("Universal recipient", TDB), Paragraph("AB-positive (no antibodies in plasma)", TDS)],
    [Paragraph("Pre-op autologous donation", TDB), Paragraph("Up to 5 units. First: 40 days before. Last: 3 days before. Interval: 3–4 days.", TDS)],
    [Paragraph("Cell salvage contraindications", TDB), Paragraph("Cancer surgery, infected field, bowel contamination (risk of spreading pathology)", TDS)],
    [Paragraph("IgA deficiency + transfusion", TDB), Paragraph("Risk of anaphylaxis. Must use IgA-deficient blood or washed products.", TDS)],
    [Paragraph("Shock Class III vs IV fluid Rx", TDB), Paragraph("Both need crystalloid + blood. Class IV is more urgent — consider immediate massive transfusion protocol.", TDS)],
    [Paragraph("Blood substitutes status", TDB), Paragraph("HBOCs and PFCs — both still EXPERIMENTAL. Not in routine clinical use.", TDS)],
]
story.append(make_table(master_data, [5.5*cm, 12.1*cm]))
story.append(SP(6))

# ── Sources footer ────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=1, color=BORDER))
story.append(SP(3))
story.append(Paragraph(
    "<b>Sources:</b> Goldman-Cecil Medicine 2 Vol Set (pp. 2824–2900)  |  "
    "Bailey &amp; Love's Surgery 28th ed. (pp. 42–44)  |  "
    "Barash Clinical Anesthesia 9th ed. (pp. 1315–1316)  |  "
    "Henry's Clinical Diagnosis &amp; Laboratory Methods  |  "
    "Scott-Brown's Surgery (p. 293)  |  Miller's Anesthesia 10th ed.",
    FOOTER_ST))

# ── Build ─────────────────────────────────────────────────────────
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print("Done:", OUTPUT)
Running Command

cd /home/daytona/workspace/blood-transfusion-qr && python3 make_theory_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/blood-transfusion-qr/Blood_Transfusion_Theory_Exam_StudyGuide.pdf

Your theory exam study guide PDF is ready:

Blood Transfusion Theory Exam StudyGuide

PDF Document · PDF


What's inside — built specifically for theory exams:
SectionContent
① Blood ProductsLong-answer writing framework + master table (composition, storage, shelf life, indications) + Viva Q&A + MCQ traps
② Storage SolutionsCPDA / SAG-M — each component with mechanism and exam tip. Viva Q&A on citrate toxicity and 2,3-BPG
③ Transfusion TriggersTRICC trial explained, Hb thresholds with rationale, MCQ traps on old '10/30 rule'
④ Massive TransfusionLethal Triad (step-by-step), 7-step management table, viva on fibrinogen + dilutional coagulopathy + TXA 3-hour rule
⑤ Transfusion ReactionsFull classification, AHTR step-by-step, TACO vs TRALI head-to-head table, viva + 5 MCQ traps
⑥ Leukoreduction3 reasons, UK 1999 policy, FNHTR reduction
⑦ Blood Groups & Cross-matchingABO table, emergency blood priority (O-neg for females)
⑧ Autologous TransfusionPAD protocol (40/3/3-4 day rules), cell salvage contraindications
⑨ Shock ClassesClass I-IV with fluid management
⑩ Blood SubstitutesHBOCs + PFCs — why experimental
★ Master Revision Table29 one-line exam answers covering every tested fact
Every section follows: Core concept → Long-answer framework → Short-note bullets → Viva Q&A → MCQ traps → Memory mnemonics
This is a shared conversation. Sign in to Orris to start your own chat.