Blood transfusion and important things to know in blood bank and transfusion for a doctor

Reading File
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
I now have comprehensive data from multiple authoritative textbooks. Let me compile the complete clinical reference.

Blood Transfusion and Blood Bank: A Clinical Reference for Doctors


1. BLOOD PRODUCTS AND THEIR COMPONENTS

Packed Red Blood Cells (pRBCs)

  • Purpose: Increase oxygen-carrying capacity. NOT for nutritional benefit or wound healing.
  • Storage: 1-6°C in additive solution (ADSOL, AS-1). Shelf life 42 days.
  • Volume per unit: ~250-350 mL. Expected Hb rise: ~1 g/dL per unit in a 70 kg adult (or platelet count rise of ~5000-10,000/µL for platelets).
  • Must be ABO and Rh(D) compatible for major crossmatch.

Fresh Frozen Plasma (FFP)

  • Contents: All coagulation factors, fibrinogen, vWF, protein C/S, antithrombin.
  • Storage: Frozen at -18°C or below; shelf life 1 year. Once thawed, must be used within 24 hours (or 5 days if labeled "thawed plasma").
  • Volume: ~200-250 mL per unit.
  • Indications: Active bleeding with multiple factor deficiencies, warfarin reversal when 4-factor PCC is unavailable, massive transfusion, DIC, TTP (with plasmapheresis).
  • ABO compatibility: Should be ABO compatible (minor crossmatch). Group AB plasma = universal donor for plasma. Group A plasma is being used increasingly in massive transfusion due to scarcity of AB.
  • Key rule: FFP and cryoprecipitate are NOT interchangeable.

Cryoprecipitate

  • Contents: Fibrinogen (factor I), vWF, factor VIII, factor XIII, fibronectin - enriched fraction precipitated from thawed FFP.
  • Main indication: Hypofibrinogenemia (fibrinogen <150-200 mg/dL) - obstetric hemorrhage, cardiac surgery (CPB/ECMO), liver disease, DIC.
  • NOT indicated for isolated factor VIII or vWF deficiency anymore (factor concentrates preferred).
  • Dose: Pool of 10 units for adults → raises fibrinogen ~50-70 mg/dL.
  • Formula for precise dosing: (Desired fibrinogen - Initial fibrinogen) × Plasma volume (mL) ÷ 100 ÷ 250 mg/unit.
  • ABO compatibility not strictly required for adult cryoprecipitate.

Platelets

  • Storage: 20-24°C on agitator (room temperature). Shelf life 5 days (limited by bacterial contamination risk).
  • Types: Whole blood-derived (pooled 4-6 units = 1 therapeutic dose) vs. apheresis platelets (single donor, 1 unit = equivalent of ~6 whole blood-derived).
  • Indications for prophylactic transfusion:
    • Plt <10,000/µL in a stable patient
    • Plt <20,000/µL with fever/infection
    • Plt <50,000/µL before invasive procedure or surgery
    • Plt <100,000/µL for neurosurgery or ophthalmic surgery
  • Platelet refractoriness: Failure to achieve expected increment (1-hour corrected count increment <5,000-7,500). Immune cause: HLA alloantibodies (most common), HPA alloantibodies. Non-immune: DIC, sepsis, splenomegaly, fever.

Granulocytes

  • Rarely used; for severe neutropenia with life-threatening infections refractory to antibiotics.
  • Must be irradiated. Very short shelf life (~24 hours).

2. PRETRANSFUSION TESTING

Type and Screen (T&S)

  • ABO/D typing + antibody screen (unexpected alloantibody detection).
  • Specimen validity: 3 days for patients transfused or pregnant in the last 3 months. Up to 1 month for those with no recent transfusion/pregnancy.
  • Most hemolytic transfusion reactions are caused by misidentification or labeling errors - proper patient identification is the single most important safety step.

Crossmatch

  • Electronic crossmatch: Computer confirms ABO/D compatibility; used when antibody screen is negative and two concordant ABO typings exist.
  • Serologic crossmatch: Mix patient plasma + donor RBCs. Can be immediate spin (IS) or AHG (antihuman globulin) phase.
  • If patient has a clinically significant antibody, antigen-negative units must be identified - this can take hours to days.

ABO/Rh Compatibility Rules

Blood GroupCan Receive pRBCs FromCan Receive Plasma From
OO only (universal donor RBC)O, A, B, AB
AA, OA, AB
BB, OB, AB
ABA, B, O, AB (universal recipient)AB only (universal donor plasma)
  • Emergency: Give group O pRBCs and group AB plasma before type is known.
  • Rh(D)-negative females of childbearing potential must receive D-negative pRBCs to prevent sensitization and risk of hemolytic disease of the fetus/newborn (HDN). Risk of anti-D formation from D+ unit: ~10-20%.

3. TRANSFUSION THRESHOLDS (EVIDENCE-BASED)

pRBC Transfusion Triggers

Patient PopulationHemoglobin Threshold
Stable, non-cardiac ICU7 g/dL (restrictive)
Stable surgical patients7-8 g/dL
GI bleeding (no CAD)8 g/dL
Known/symptomatic CAD>9 g/dL (liberal threshold)
Portal hypertension bleeding7-8 g/dL (avoid overtransfusion - worsens portal pressure)
Symptomatic anemia (any Hb)Transfuse based on symptoms
  • Healthy volunteers can tolerate Hb as low as 5.1 g/dL short-term through increased cardiac output.
  • Restrictive strategy (threshold 7-8 g/dL) has shown lower all-cause mortality and rebleeding vs. liberal strategy in RCTs for GI bleeding.

Platelet Thresholds

  • <10,000/µL: prophylactic transfusion
  • <50,000/µL: before invasive procedure
  • <100,000/µL: neurosurgery/eye surgery

Plasma/Cryoprecipitate

  • FFP: INR >1.5-2.0 with active bleeding or before procedure; NOT for routine coagulopathy correction without bleeding.
  • Cryoprecipitate: fibrinogen <150-200 mg/dL with bleeding.
  • Warfarin reversal: 4-factor PCC is the definitive agent; FFP is second-line only if 4F-PCC unavailable.

4. SPECIAL BLOOD PRODUCT MODIFICATIONS

Leukoreduction (Leukodepleted)

  • Removal of white cells by filtration.
  • Indications: Prevent febrile nonhemolytic reactions, reduce CMV transmission risk, prevent HLA alloimmunization (platelet-refractory patients), reduce immunomodulation.
  • Now standard practice in most blood banks (universal leukoreduction).

Irradiation

  • Gamma or X-ray irradiation inactivates T-lymphocytes to prevent transfusion-associated graft-versus-host disease (TA-GvHD).
  • Indications:
    • Immunocompromised patients (congenital immunodeficiency, bone marrow transplant, HSCT)
    • Directed donations from blood relatives
    • HLA-matched platelets
    • Intrauterine transfusions
    • Patients receiving purine analogues (fludarabine, cladribine)
  • TA-GvHD is rare (<1:10,000,000) but case fatality rate is very high (~90%).

CMV-Negative (Seronegative) Blood

  • Indications: CMV-negative patients who are pregnant, or immunocompromised awaiting transplant.
  • Leukoreduced blood is considered CMV-safe (equivalent), as CMV is cell-associated.

Washed Red Cells / Platelets

  • Removes plasma proteins (IgA, IgE).
  • Indications: IgA-deficient patients with anti-IgA antibodies (anaphylaxis risk), severe recurrent allergic reactions.

5. TRANSFUSION REACTIONS

Risk per Unit Transfused (USA data)

ReactionRisk
Febrile nonhemolytic (FNHTR)~1 : 1,100
Allergic (mild)~1 : 100-1,200
Allergic (severe/anaphylaxis)~1 : 20,000-50,000
Acute hemolytic (AHTR)~1 : 6,000-110,000
TACO~1 : 9,000
Delayed hemolytic (DHTR)~1 : 3,000-32,000
TRALI~1 : 5,000-140,000
Bacterial sepsis (RBC)~1 : 250,000
Bacterial sepsis (platelets)~1 : 25,000
Fatal hemolytic~1 : 100,000-600,000
TA-GvHD<1 : 10,000,000
Sources: Goldman-Cecil Medicine, Quick Compendium of Clinical Pathology, Tietz Textbook of Laboratory Medicine

ACUTE Transfusion Reactions

Febrile Non-Hemolytic Transfusion Reaction (FNHTR)

  • Most common transfusion reaction (~1:200, some series).
  • Definition: Temperature rise ≥1°C (or 2°F) from pretransfusion baseline, without hemolysis or other cause.
  • Mechanism: Cytokines accumulated in stored product from donor WBCs; also recipient antibodies against donor HLA antigens.
  • Management: Stop transfusion → rule out hemolysis (check for clerical errors, DAT, pink plasma, hemoglobinuria) → give antipyretics → restart if non-hemolytic confirmed.
  • Prevention: Prestorage leukoreduction dramatically reduces incidence.

Allergic Transfusion Reaction

  • Second most common.
  • Mechanism: IgE-mediated hypersensitivity to donor plasma proteins.
  • Special case: IgA-deficient patients (1:700 population) with anti-IgA antibodies can have anaphylaxis.
  • Mild: Urticaria, pruritus, flushing - stop, give antihistamine, can restart at slower rate.
  • Severe/Anaphylaxis: Stop permanently. Epinephrine + H1 antihistamine + corticosteroids.
  • Prevention: Premedicate with antihistamines (only in patients with prior reactions - routine premedication is not evidence-based). For anti-IgA: IgA-deficient donor units or washed products.

Acute Hemolytic Transfusion Reaction (AHTR)

  • Most life-threatening acute reaction. Mainly ABO incompatibility (usually a clerical error).
  • Mechanism: Recipient preformed IgM antibodies against donor RBC antigens → complement activation → intravascular hemolysis.
  • Symptoms: Fever, chills, back/flank pain, chest pain, hypotension, hemoglobinuria (pink/red urine), shock, DIC, renal failure. In anesthetized patients: unexplained hypotension, hemoglobinuria.
  • Management:
    1. STOP transfusion immediately
    2. Keep IV line open with normal saline
    3. Send samples for: repeat ABO typing (pre- and post-transfusion), DAT, plasma hemoglobin (visual pink), urine hemoglobin (dipstick), coagulation screen, bilirubin
    4. Send donor unit and recipient sample to blood bank
    5. Maintain urine output (fluids ± diuretics)
    6. Treat DIC if present
    7. Report to blood bank physician
  • Fatal cases must be reported to FDA within 7 days.

Transfusion-Related Acute Lung Injury (TRALI)

  • Leading cause of transfusion-related death in the USA.
  • Definition: New acute lung injury within 6 hours of cessation of transfusion, with bilateral infiltrates on CXR, PaO2/FiO2 <300, no pre-existing ALI.
  • Mechanism: Donor WBC antibodies (HLA class I/II, HNA antibodies) activate recipient neutrophils + pulmonary endothelium → non-cardiogenic pulmonary edema. "Two-hit" model.
  • Most commonly after: Platelet transfusions, high-plasma products.
  • Risk factors: Female donors (especially multiparous), ICU patients, liver surgery, alcohol use, shock.
  • Management: Supportive - oxygen, mechanical ventilation if needed. No diuretics (not fluid overload). Usually resolves within 48-96 hours.
  • Prevention: Male-only plasma/platelets, HLA antibody-negative female donors, nulliparous female donors; solvent/detergent-treated plasma.

Transfusion-Associated Circulatory Overload (TACO)

  • Incidence: ~1:9,000 (likely underreported).
  • Mechanism: Volume overload from transfusion in susceptible patients.
  • Risk factors: Age >70, positive fluid balance >3L, BNP >1000 pg/mL post-transfusion, multiple units transfused in 6 hours, cardiac/renal disease.
  • Symptoms: Dyspnea, tachypnea, orthopnea, cough, hypertension, hypoxia - often at end of transfusion or up to 12 hours after.
  • Diagnosis: Clinical + elevated BNP/NT-proBNP + cardiogenic pulmonary edema on CXR.
  • Management: Stop or slow transfusion. Sit patient upright. Oxygen. Diuretics (furosemide).
  • Key distinction from TRALI: TACO = hypertension, elevated BNP, responds to diuretics. TRALI = normotension or hypotension, no BNP elevation, does NOT respond to diuretics.

Hypotensive Transfusion Reaction

  • Isolated hypotension (SBP drop >30 mmHg) without other features.
  • Associated with ACE inhibitor use + bedside leukoreduction filters (bradykinin pathway).

DELAYED Transfusion Reactions

Delayed Hemolytic Transfusion Reaction (DHTR)

  • Onset: 5-14 days after transfusion.
  • Mechanism: Recall of alloantibodies (anamnestic response) to previously sensitized antigens. IgG mediated → extravascular hemolysis.
  • Most commonly implicated antigens: Kidd (Jk), E, c (Rh), Kell, Duffy antigens.
  • Kidd antibodies cause the most severe DHTR (can be intravascular).
  • Findings: Unexpected fall in Hb, positive DAT (mixed field), hyperbilirubinemia, microspherocytes, new alloantibody on screen.
  • Management: Identify causative antibody. Avoid further exposure. Usually self-limiting.

Delayed Serologic Transfusion Reaction (DSTR)

  • New detectable alloantibody post-transfusion WITHOUT hemolysis.
  • More common than DHTR (~3:1 ratio).

Transfusion-Associated GvHD (TA-GvHD)

  • Donor T lymphocytes engraft in immunocompromised host → attack host tissues.
  • Onset: 8-10 days post-transfusion.
  • Features: Fever, skin rash/desquamation, diarrhea, liver dysfunction, aplastic pancytopenia (bone marrow destruction - key distinguishing feature from regular GvHD).
  • Case fatality ~90% - prevention by irradiation is critical.

Post-Transfusion Purpura (PTP)

  • Thrombocytopenia 5-10 days after transfusion.
  • Anti-HPA-1a (PLA1) antibody destroys both donor AND recipient's own platelets.
  • Treat with IVIg; plasma exchange.

Transfusion-Associated Dyspnea (TAD)

  • Respiratory distress within 24 hours that doesn't meet TRALI or TACO criteria.

Transfusion-Transmitted Infections (Current Residual Risks, USA)

PathogenResidual Risk
HIV~1 : 1,800,000
HCV~1 : 1,600,000
HBV~1 : 300,000-1,500,000
HTLV~1 : 3,300,000
CMVRare (leukoreduction protective)
Bacterial (platelets)~1 : 25,000
Bacterial (RBCs)~1 : 250,000
  • Bacteria are now the most common transfusion-transmitted infection.
  • Platelet bacteremia: staphylococci (room temperature storage). RBC bacteremia: psychrophilic organisms (Yersinia, Pseudomonas) that grow at 4°C.
  • Window period testing: HIV MP-NAT 9 days; HCV MP-NAT 7 days.

6. TRANSFUSION REACTION WORKUP ALGORITHM

When a transfusion reaction is suspected:
  1. STOP the transfusion immediately
  2. Keep IV line open with normal saline
  3. Clerical check: Compare patient ID band, compatibility tag, and unit label - most severe hemolytic reactions result from clerical errors
  4. Notify the blood bank and attending physician
  5. Send to lab:
    • Post-transfusion blood sample (EDTA and clot tube)
    • Urine sample (check for hemoglobinuria)
    • Return the blood unit and administration set to blood bank
  6. Blood bank workup:
    • Repeat ABO/Rh typing on post-transfusion sample
    • DAT on post-transfusion sample (compare to pre-transfusion)
    • Visual inspection of plasma for hemolysis (pink/red)
    • Urine dipstick for hemoglobin (hemoglobinuria without hematuria = intravascular hemolysis)
    • Bilirubin (elevated up to 24-36 hours post-event)
  7. Transfusion service physician reviews and determines if reaction was hemolytic
  8. If hemolytic → identify antibody → provide antigen-negative, crossmatch compatible blood going forward
  9. Reporting: Fatalities and serious morbidity → FDA ASAP by phone + written report within 7 days

7. MASSIVE TRANSFUSION

Definition

  • Transfusion of ≥1 total blood volume in 24 hours, OR
  • ≥10 pRBC units in 24 hours in an average adult.

Massive Transfusion Protocol (MTP)

  • Goal: Replace whole blood with near-physiologic ratios of components.
  • Standard 1:1:1 ratio (plasma : platelets [whole blood-derived] : pRBCs) or 1:1:2.
  • Typical MTP pack: 6 pRBC + 6 FFP + 1 apheresis platelet (issued as a pack).
  • Blood bank continues assembling successive packs until clinically terminated.
  • Consider adding cryoprecipitate/fibrinogen concentrate if fibrinogen is low.
  • PROPPR trial evidence supports 1:1:1 ratio (plasma:platelets:RBC) reducing 24-hour mortality.

Complications of Massive Transfusion ("Lethal Triad")

  • Hypothermia: Use blood warmers; all fluids warmed.
  • Acidosis: Citrate in blood products → metabolic acidosis.
  • Coagulopathy (dilutional): From large pRBC-only resuscitation - prevented by balanced MTP ratios.
  • Hypocalcemia: Citrate chelates ionized calcium → monitor and replace calcium.
  • Hyperkalemia: Stored RBCs release K⁺; risk in neonates, patients with renal failure.
  • Transfusion-associated circulatory overload (TACO).

8. SPECIAL CLINICAL SCENARIOS

Sickle Cell Disease

  • Use phenotypically matched RBCs (C, E, Kell, Duffy, Kidd) to minimize alloimmunization.
  • Risk of hyperhemolysis syndrome - both donor and patient cells hemolyze; paradoxically avoid further transfusion.
  • Exchange transfusion (apheresis) preferred for acute stroke prevention/treatment.

Jehovah's Witnesses

  • Refuse allogeneic blood transfusions on religious grounds.
  • Discuss: cell salvage (intraoperative), erythropoiesis-stimulating agents (EPO), iron supplementation, fibrin glue.
  • Some accept autologous blood if in a closed circuit. Respect patient autonomy; obtain informed refusal.
  • Emergency provisions vary by jurisdiction - ethics consultation for unconscious patients.

Autoimmune Hemolytic Anemia (AIHA)

  • Warm autoantibodies make crossmatch difficult (all units may appear incompatible).
  • Use "least incompatible" units after ruling out underlying alloantibodies.
  • Treat the underlying cause concurrently.

Neonatal/Perinatal Transfusion

  • Hemolytic disease of the fetus and newborn (HDN): ABO (usually mild) or Rh incompatibility (can be severe). Test maternal serum; treat with phototherapy or exchange transfusion.
  • Intrauterine transfusion (IUT): Must be irradiated, CMV-safe, pRBCs; group O, Rh-negative.

Plasma Exchange / Therapeutic Apheresis

  • TTP: Plasma exchange with FFP replacement - emergent, life-saving.
  • Hyperviscosity syndrome, myasthenia gravis, Guillain-Barré: Plasma exchange.

9. AUTOLOGOUS TRANSFUSION

Types

  • Preoperative autologous donation (PAD): Patient donates their own blood weeks before elective surgery.
  • Acute normovolemic hemodilution (ANH): Blood withdrawn at start of surgery, replaced with crystalloid; reinfused after major blood loss.
  • Intraoperative cell salvage: Shed blood collected, washed, and reinfused - contraindicated in cancer surgery (risk of tumor cell reinfusion) and infected fields.

10. BLOOD PRODUCT STORAGE SUMMARY

ProductStorage TemperatureShelf LifeNotes
pRBC1-6°C42 daysAdditive solution (ADSOL/AS-1)
FFP-18°C or below1 yearThawed: 24h (or 5 days as "thawed plasma")
Cryoprecipitate-18°C or below1 yearThawed: 6h (pooled); 4h (single)
Platelets20-24°C (agitated)5 daysRoom temp - highest bacterial risk
Granulocytes20-24°C24 hoursMust irradiate
Whole blood1-6°C21-35 daysDepending on anticoagulant

11. KEY BLOOD BANK CONCEPTS

  • Direct Antiglobulin Test (DAT / Direct Coombs): Detects antibody or complement bound to patient's own RBCs. Positive in: AHTR, AIHA, HDN, drug-induced hemolysis.
  • Indirect Antiglobulin Test (IAT / Indirect Coombs): Detects free antibodies in patient serum against RBC antigens. Used for antibody screening and crossmatch.
  • ABO discrepancy: Discordance between forward (cell) and reverse (serum) typing. Must be resolved before transfusion.
  • Polyagglutination: RBCs agglutinated by almost all adult plasma; due to T/Tn activation or inherited phenotypes.
  • Platelet corrected count increment (CCI): [(Post-pre PLT count) × BSA] ÷ number of platelets transfused × 10¹¹. CCI <5,000-7,500 at 1 hour = immune refractoriness.

Sources:
  • Tietz Textbook of Laboratory Medicine, 7th Edition (chapters 92, 93)
  • Goldman-Cecil Medicine, International Edition (chapter 162)
  • Quick Compendium of Clinical Pathology, 5th Edition (chapter 2.8)
  • Rosen's Emergency Medicine, 10th Edition
  • Miller's Anesthesia, 10th Edition

Create a summary PDF with blood transfusion protocols and reaction flowcharts

Reading File
Running Command

ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"

Running Command

mkdir -p /tmp/workspace/blood-transfusion-pdf && echo "done"

Writing File

~/blood-transfusion-pdf/generate_pdf.py

#!/usr/bin/env python3
"""
Blood Transfusion Protocols & Reaction Flowcharts - Clinical Reference PDF
Generated with ReportLab
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    PageBreak, HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import Flowable
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon, Arrow
from reportlab.graphics import renderPDF
import os

OUTPUT_PATH = "/tmp/workspace/blood-transfusion-pdf/Blood_Transfusion_Protocols.pdf"

# ─── Color Palette ────────────────────────────────────────────────────────────
DEEP_RED     = colors.HexColor("#8B0000")
MED_RED      = colors.HexColor("#C0392B")
LIGHT_RED    = colors.HexColor("#FADBD8")
DARK_BLUE    = colors.HexColor("#1A3C5E")
MED_BLUE     = colors.HexColor("#2E86C1")
LIGHT_BLUE   = colors.HexColor("#D6EAF8")
TEAL         = colors.HexColor("#117A65")
LIGHT_TEAL   = colors.HexColor("#D1F2EB")
ORANGE       = colors.HexColor("#D35400")
LIGHT_ORANGE = colors.HexColor("#FAE5D3")
YELLOW_BG    = colors.HexColor("#FEF9E7")
DARK_GREY    = colors.HexColor("#2C3E50")
MID_GREY     = colors.HexColor("#7F8C8D")
LIGHT_GREY   = colors.HexColor("#ECF0F1")
WHITE        = colors.white
BLACK        = colors.black
GOLD         = colors.HexColor("#F39C12")

W, H = A4  # 595.27 x 841.89 pts

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

def make_style(name, parent='Normal', **kwargs):
    return ParagraphStyle(name, parent=styles[parent], **kwargs)

TITLE_STYLE = make_style('DocTitle', fontSize=24, textColor=WHITE,
                          fontName='Helvetica-Bold', alignment=TA_CENTER, leading=30)
SUBTITLE_STYLE = make_style('DocSubtitle', fontSize=11, textColor=colors.HexColor("#BDC3C7"),
                             fontName='Helvetica', alignment=TA_CENTER, leading=16)
SECTION_STYLE = make_style('SectionHead', fontSize=13, textColor=WHITE,
                            fontName='Helvetica-Bold', alignment=TA_LEFT, leading=18)
BODY_STYLE = make_style('Body', fontSize=8.5, textColor=DARK_GREY,
                         fontName='Helvetica', leading=13, spaceAfter=4)
BODY_BOLD = make_style('BodyBold', fontSize=8.5, textColor=DARK_GREY,
                        fontName='Helvetica-Bold', leading=13)
SMALL_STYLE = make_style('Small', fontSize=7.5, textColor=MID_GREY,
                          fontName='Helvetica', leading=11)
TABLE_HDR = make_style('TblHdr', fontSize=8, textColor=WHITE,
                        fontName='Helvetica-Bold', alignment=TA_CENTER, leading=11)
TABLE_CELL = make_style('TblCell', fontSize=8, textColor=DARK_GREY,
                         fontName='Helvetica', leading=11)
TABLE_CELL_BOLD = make_style('TblCellBold', fontSize=8, textColor=DARK_GREY,
                               fontName='Helvetica-Bold', leading=11)
TABLE_CELL_CENTER = make_style('TblCellC', fontSize=8, textColor=DARK_GREY,
                                fontName='Helvetica', alignment=TA_CENTER, leading=11)
WARNING_STYLE = make_style('Warning', fontSize=8.5, textColor=MED_RED,
                            fontName='Helvetica-Bold', leading=13)
FLOW_TEXT = make_style('FlowText', fontSize=8, textColor=DARK_GREY,
                        fontName='Helvetica', alignment=TA_CENTER, leading=11)
FLOW_TEXT_BOLD = make_style('FlowTextBold', fontSize=8, textColor=WHITE,
                              fontName='Helvetica-Bold', alignment=TA_CENTER, leading=11)
CAPTION = make_style('Caption', fontSize=7.5, textColor=MID_GREY,
                      fontName='Helvetica-Oblique', alignment=TA_CENTER, leading=10)


# ─── Header/Footer ────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    # Footer bar
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, 0, W, 18*mm, fill=1, stroke=0)
    canvas.setFillColor(WHITE)
    canvas.setFont('Helvetica', 7.5)
    canvas.drawString(15*mm, 6*mm, "Blood Transfusion Protocols & Reference  |  Clinical Summary")
    canvas.drawRightString(W - 15*mm, 6*mm, f"Page {doc.page}")
    canvas.setFillColor(GOLD)
    canvas.rect(0, 17*mm, W, 1.5*mm, fill=1, stroke=0)
    canvas.restoreState()

def on_first_page(canvas, doc):
    on_page(canvas, doc)


# ─── Custom Flowables ─────────────────────────────────────────────────────────
class SectionHeader(Flowable):
    def __init__(self, text, color=DARK_BLUE, width=None):
        super().__init__()
        self.text = text
        self.color = color
        self.width = width or (W - 30*mm)
        self.height = 22*mm

    def draw(self):
        c = self.canv
        c.setFillColor(self.color)
        c.roundRect(0, 2*mm, self.width, 16*mm, 3*mm, fill=1, stroke=0)
        # Accent strip
        c.setFillColor(GOLD)
        c.rect(0, 2*mm, 4*mm, 16*mm, fill=1, stroke=0)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 12)
        c.drawString(8*mm, 8.5*mm, self.text)

    def wrap(self, avW, avH):
        return self.width, self.height


class BoxedNote(Flowable):
    def __init__(self, text, bg=LIGHT_RED, border=MED_RED, icon="!", width=None):
        super().__init__()
        self.text = text
        self.bg = bg
        self.border = border
        self.icon = icon
        self.width = width or (W - 30*mm)
        self.height = 18*mm

    def draw(self):
        c = self.canv
        c.setFillColor(self.bg)
        c.setStrokeColor(self.border)
        c.setLineWidth(1.5)
        c.roundRect(0, 0, self.width, 14*mm, 2*mm, fill=1, stroke=1)
        # Icon circle
        c.setFillColor(self.border)
        c.circle(7*mm, 7*mm, 4*mm, fill=1, stroke=0)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 9)
        c.drawCentredString(7*mm, 5*mm, self.icon)
        # Text
        c.setFillColor(DARK_GREY)
        c.setFont('Helvetica-Bold', 8)
        c.drawString(14*mm, 5.5*mm, self.text)

    def wrap(self, avW, avH):
        return self.width, self.height


class FlowchartBox(Flowable):
    """Renders a complete flowchart as a drawing"""
    def __init__(self, drawing, width, height):
        super().__init__()
        self._drawing = drawing
        self.width = width
        self.height = height

    def draw(self):
        renderPDF.draw(self._drawing, self.canv, 0, 0)

    def wrap(self, avW, avH):
        return self.width, self.height


# ─── Helper: draw flowchart box ───────────────────────────────────────────────
def fc_rect(d, x, y, w, h, text, fill=DARK_BLUE, text_color=WHITE, font_size=8, radius=4):
    d.add(Rect(x, y, w, h, rx=radius, ry=radius, fillColor=fill,
               strokeColor=colors.HexColor("#BDC3C7"), strokeWidth=0.8))
    lines = text.split('\n')
    total_h = len(lines) * (font_size + 2)
    start_y = y + h/2 + total_h/2 - font_size
    for i, line in enumerate(lines):
        d.add(String(x + w/2, start_y - i*(font_size+2), line,
                     fontName='Helvetica-Bold' if i == 0 else 'Helvetica',
                     fontSize=font_size, fillColor=text_color,
                     textAnchor='middle'))

def fc_diamond(d, cx, cy, w, h, text, fill=ORANGE, text_color=WHITE, font_size=7.5):
    points = [cx, cy+h/2, cx+w/2, cy, cx, cy-h/2, cx-w/2, cy]
    d.add(Polygon(points, fillColor=fill, strokeColor=colors.HexColor("#BDC3C7"), strokeWidth=0.8))
    lines = text.split('\n')
    for i, line in enumerate(lines):
        d.add(String(cx, cy + (len(lines)-1)*(font_size+1)/2 - i*(font_size+1),
                     line, fontName='Helvetica-Bold', fontSize=font_size,
                     fillColor=text_color, textAnchor='middle'))

def fc_arrow(d, x1, y1, x2, y2, color=DARK_GREY, label='', label_side='right'):
    d.add(Line(x1, y1, x2, y2, strokeColor=color, strokeWidth=1.2))
    # Arrowhead
    if y2 < y1:  # downward
        d.add(Polygon([x2, y2, x2-4, y2+7, x2+4, y2+7], fillColor=color, strokeWidth=0))
    elif y2 > y1:  # upward
        d.add(Polygon([x2, y2, x2-4, y2-7, x2+4, y2-7], fillColor=color, strokeWidth=0))
    elif x2 > x1:  # right
        d.add(Polygon([x2, y2, x2-7, y2+4, x2-7, y2-4], fillColor=color, strokeWidth=0))
    elif x2 < x1:  # left
        d.add(Polygon([x2, y2, x2+7, y2+4, x2+7, y2-4], fillColor=color, strokeWidth=0))
    if label:
        lx = (x1+x2)/2 + (5 if label_side=='right' else -5)
        ly = (y1+y2)/2
        d.add(String(lx, ly, label, fontName='Helvetica', fontSize=7,
                     fillColor=ORANGE, textAnchor='start' if label_side=='right' else 'end'))

def fc_label(d, x, y, text, color=TEAL, font_size=7):
    d.add(String(x, y, text, fontName='Helvetica-Bold', fontSize=font_size,
                 fillColor=color, textAnchor='middle'))


# ─── Build Flowchart: Transfusion Reaction Management ─────────────────────────
def build_reaction_flowchart():
    dw, dh = 480, 560
    d = Drawing(dw, dh)
    d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#F8FBFF"), strokeColor=LIGHT_GREY, strokeWidth=1))

    cx = dw / 2
    bw, bh = 200, 28
    # Start
    fc_rect(d, cx-bw/2, dh-50, bw, bh, "SUSPECT TRANSFUSION REACTION\n(fever, chills, pain, dyspnea, hemoglobinuria)",
            fill=MED_RED, font_size=7.5)
    fc_arrow(d, cx, dh-50, cx, dh-50-18)

    # Step 1
    y1 = dh - 110
    fc_rect(d, cx-bw/2, y1, bw, bh, "STEP 1: STOP TRANSFUSION IMMEDIATELY\nKeep IV line open with 0.9% NaCl",
            fill=DARK_BLUE, font_size=7.5)
    fc_arrow(d, cx, y1, cx, y1-18)

    # Step 2
    y2 = dh - 170
    fc_rect(d, cx-bw/2, y2, bw, bh, "STEP 2: CLERICAL CHECK\nVerify patient ID, unit label, compatibility tag",
            fill=DARK_BLUE, font_size=7.5)
    fc_arrow(d, cx, y2, cx, y2-18)

    # Step 3
    y3 = dh - 230
    fc_rect(d, cx-bw/2, y3, bw, bh, "STEP 3: NOTIFY BLOOD BANK & PHYSICIAN\nSend unit + tubing back to blood bank",
            fill=DARK_BLUE, font_size=7.5)
    fc_arrow(d, cx, y3, cx, y3-18)

    # Step 4 - Labs
    y4 = dh - 290
    fc_rect(d, cx-bw/2, y4, bw, bh, "STEP 4: SEND SAMPLES\nPost-Tx blood (EDTA+clot), urine sample",
            fill=TEAL, font_size=7.5)
    fc_arrow(d, cx, y4, cx, y4-25)

    # Diamond - hemolysis?
    y5 = dh - 348
    fc_diamond(d, cx, y5, 160, 38, "Evidence of\nHEMOLYSIS?", fill=ORANGE, font_size=8)
    
    # YES branch (left)
    fc_arrow(d, cx-80, y5, cx-80-50, y5, label='YES', label_side='left')
    fc_rect(d, cx-80-50-90, y5-14, 90, 55, "HEMOLYTIC RXN\n- Supportive care\n- Maintain UO\n- Treat DIC\n- Repeat ABO/DAT\n- Report to FDA",
            fill=MED_RED, font_size=7)

    # NO branch (right)
    fc_arrow(d, cx+80, y5, cx+80+50, y5, label='NO', label_side='right')
    fc_rect(d, cx+80+50, y5-14, 90, 40, "NON-HEMOLYTIC\n- Antipyretics\n- Antihistamine\n- May restart", fill=TEAL, font_size=7)

    # Bottom diamond - type of non-hemolytic
    fc_arrow(d, cx, y5-19, cx, y5-19-20)
    y6 = y5 - 75
    fc_diamond(d, cx, y6, 170, 38, "TRALI or TACO?", fill=DARK_BLUE, font_size=8)

    # TRALI
    fc_arrow(d, cx-85, y6, cx-85-40, y6, label='TRALI', label_side='left')
    fc_rect(d, cx-85-40-90, y6-14, 90, 40, "TRALI:\n- O2/ventilation\n- NO diuretics\n- Supportive",
            fill=MED_BLUE, font_size=7)

    # TACO
    fc_arrow(d, cx+85, y6, cx+85+40, y6, label='TACO', label_side='right')
    fc_rect(d, cx+85+40, y6-14, 90, 40, "TACO:\n- O2 + sit upright\n- Furosemide\n- Slow/stop Tx",
            fill=TEAL, font_size=7)

    # Bottom note
    d.add(Rect(10, 8, dw-20, 22, fillColor=LIGHT_RED, strokeColor=MED_RED, strokeWidth=0.8, rx=3, ry=3))
    d.add(String(dw/2, 15, "⚠  Fatalities must be reported to FDA by phone ASAP + written report within 7 days",
                  fontName='Helvetica-Bold', fontSize=7.5, fillColor=MED_RED, textAnchor='middle'))

    return FlowchartBox(d, dw, dh)


# ─── Build Flowchart: pRBC Transfusion Decision ───────────────────────────────
def build_prbc_flowchart():
    dw, dh = 480, 420
    d = Drawing(dw, dh)
    d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#F8FBFF"), strokeColor=LIGHT_GREY, strokeWidth=1))

    cx = dw / 2
    bw, bh = 200, 28

    # Start
    fc_rect(d, cx-bw/2, dh-50, bw, bh, "PATIENT REQUIRES BLOOD TRANSFUSION\nCheck Hb, clinical status, consent",
            fill=DARK_BLUE, font_size=7.5)
    fc_arrow(d, cx, dh-50, cx, dh-50-18)

    # Emergency?
    y1 = dh - 115
    fc_diamond(d, cx, y1, 180, 40, "LIFE-THREATENING\nHEMORRHAGE?", fill=MED_RED, font_size=8)
    
    # YES - emergency
    fc_arrow(d, cx+90, y1, cx+90+40, y1, label='YES', label_side='right')
    fc_rect(d, cx+90+40, y1-14, 95, 40, "EMERGENCY:\nGroup O pRBCs\n+ Group AB plasma\n(pre-crossmatch)",
            fill=MED_RED, font_size=7)

    # NO - stable
    fc_arrow(d, cx, y1-20, cx, y1-20-18, label='NO')
    y2 = dh - 198
    fc_rect(d, cx-bw/2, y2, bw, bh, "TYPE & SCREEN / CROSSMATCH\nCheck ABO/Rh, antibody screen",
            fill=TEAL, font_size=7.5)
    fc_arrow(d, cx, y2, cx, y2-18)

    # Hb threshold decision
    y3 = dh - 265
    fc_diamond(d, cx, y3, 190, 50, "Hb THRESHOLD\n(see table)", fill=ORANGE, font_size=8)

    # Branches
    x_left = cx - 140
    x_right = cx + 140

    # Hb < 7 (general)
    fc_arrow(d, x_left, y3+10, x_left, y3+10-15)
    fc_rect(d, x_left-70, y3+10-15-35, 140, 40,
            "Hb < 7 g/dL\nStable patient\n→ Transfuse 1 unit\nRecheck Hb",
            fill=DARK_BLUE, font_size=7)

    # Hb < 8 (post-op/CAD)
    fc_arrow(d, cx, y3-25, cx, y3-25-15)
    fc_rect(d, cx-70, y3-25-15-35, 140, 40,
            "Hb < 8 g/dL\nCAD / surgical\n→ Transfuse 1 unit\nRecheck Hb",
            fill=MED_BLUE, font_size=7)

    # Symptoms
    fc_arrow(d, x_right, y3+10, x_right, y3+10-15)
    fc_rect(d, x_right-70, y3+10-15-35, 140, 40,
            "Symptomatic anemia\n(any Hb level)\n→ Transfuse\nbased on symptoms",
            fill=TEAL, font_size=7)

    # Bottom note
    d.add(Rect(10, 8, dw-20, 22, fillColor=LIGHT_BLUE, strokeColor=MED_BLUE, strokeWidth=0.8, rx=3, ry=3))
    d.add(String(dw/2, 15,
                  "One unit at a time strategy: transfuse 1 unit, reassess Hb and symptoms before next unit",
                  fontName='Helvetica-Bold', fontSize=7.5, fillColor=DARK_BLUE, textAnchor='middle'))

    return FlowchartBox(d, dw, dh)


# ─── Build Flowchart: Massive Transfusion Protocol ───────────────────────────
def build_mtp_flowchart():
    dw, dh = 480, 340
    d = Drawing(dw, dh)
    d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#FFF8F8"), strokeColor=LIGHT_GREY, strokeWidth=1))

    cx = dw / 2

    # Title bar
    d.add(Rect(10, dh-38, dw-20, 28, fillColor=MED_RED, rx=4, ry=4, strokeWidth=0))
    d.add(String(cx, dh-28, "MASSIVE TRANSFUSION PROTOCOL (MTP)",
                  fontName='Helvetica-Bold', fontSize=10, fillColor=WHITE, textAnchor='middle'))

    # Trigger box
    d.add(Rect(cx-100, dh-85, 200, 32, fillColor=LIGHT_RED, strokeColor=MED_RED, strokeWidth=1, rx=3))
    d.add(String(cx, dh-63, "TRIGGER: ≥10 pRBC/24h OR 1 blood volume/24h",
                  fontName='Helvetica-Bold', fontSize=7.5, fillColor=MED_RED, textAnchor='middle'))
    d.add(String(cx, dh-74, "Massive hemorrhage (trauma, OB, surgical)",
                  fontName='Helvetica', fontSize=7.5, fillColor=DARK_GREY, textAnchor='middle'))

    fc_arrow(d, cx, dh-85, cx, dh-85-12)

    # MTP Pack
    pack_y = dh - 165
    d.add(Rect(cx-160, pack_y, 320, 52, fillColor=DARK_BLUE, rx=4, ry=4, strokeWidth=0))
    d.add(String(cx, pack_y+40, "MTP PACK  (1:1:1 RATIO)",
                  fontName='Helvetica-Bold', fontSize=9, fillColor=GOLD, textAnchor='middle'))
    d.add(String(cx, pack_y+26, "6 units pRBC  +  6 units FFP  +  1 apheresis platelet",
                  fontName='Helvetica-Bold', fontSize=8.5, fillColor=WHITE, textAnchor='middle'))
    d.add(String(cx, pack_y+14, "Consider: Cryoprecipitate (if fibrinogen <150 mg/dL)  |  TXA (tranexamic acid)",
                  fontName='Helvetica', fontSize=7.5, fillColor=colors.HexColor("#AED6F1"), textAnchor='middle'))

    fc_arrow(d, cx, pack_y, cx, pack_y-14)

    # Complications row
    comp_y = pack_y - 75
    comps = [
        ("HYPOTHERMIA", "Use blood warmers", DARK_BLUE),
        ("HYPOCALCEMIA", "Monitor & replace\nionized Ca²⁺", TEAL),
        ("COAGULOPATHY", "Balance ratios\nAvoid pRBC-only", MED_RED),
        ("HYPERKALEMIA", "Monitor in renal\nfailure/neonates", ORANGE),
    ]
    n = len(comps)
    box_w = (dw - 40) / n
    for i, (title, note, col) in enumerate(comps):
        bx = 20 + i * box_w
        d.add(Rect(bx, comp_y, box_w-6, 50, fillColor=col, rx=3, ry=3, strokeWidth=0))
        d.add(String(bx+box_w/2-3, comp_y+37, title, fontName='Helvetica-Bold',
                      fontSize=7, fillColor=WHITE, textAnchor='middle'))
        for j, line in enumerate(note.split('\n')):
            d.add(String(bx+box_w/2-3, comp_y+24-j*10, line, fontName='Helvetica',
                          fontSize=7, fillColor=colors.HexColor("#D5D8DC"), textAnchor='middle'))

    # Bottom
    d.add(Rect(10, 8, dw-20, 20, fillColor=YELLOW_BG, strokeColor=GOLD, strokeWidth=0.8, rx=3))
    d.add(String(cx, 14, "Reassess after each pack  |  Communicate clearly with blood bank  |  Activate/deactivate MTP explicitly",
                  fontName='Helvetica-Bold', fontSize=7.5, fillColor=DARK_GREY, textAnchor='middle'))

    return FlowchartBox(d, dw, dh)


# ─── Build Summary Compatibility Table ───────────────────────────────────────
def build_abo_table():
    header = [Paragraph(t, TABLE_HDR) for t in
              ["Blood Group", "ABO Antibodies", "RBC Donors", "Plasma Donors", "Rh Note"]]
    rows = [
        ["O", "Anti-A, Anti-B", "O only\n(Universal RBC donor)", "O, A, B, AB", "O neg = universal\npRBC donor"],
        ["A", "Anti-B", "A, O", "A, AB", ""],
        ["B", "Anti-A", "B, O", "B, AB", ""],
        ["AB", "None", "A, B, O, AB\n(Universal recipient)", "AB only\n(Universal plasma)", "AB = universal\nplasma donor"],
    ]
    bgs = [LIGHT_BLUE, LIGHT_GREY, LIGHT_BLUE, LIGHT_GREY]
    data = [header]
    for i, row in enumerate(rows):
        data.append([Paragraph(str(c), TABLE_CELL_BOLD if i == 0 or i == 3 else TABLE_CELL) for c in row])

    tbl = Table(data, colWidths=[28*mm, 35*mm, 45*mm, 38*mm, 38*mm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, LIGHT_GREY, LIGHT_BLUE, colors.HexColor("#E8F8F5")]),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
        ('LEFTPADDING', (0, 0), (-1, -1), 5),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('ROWBACKGROUNDS', (0, 4), (-1, 4), [colors.HexColor("#E8F8F5")]),
    ]))
    return tbl


def build_products_table():
    header = [Paragraph(t, TABLE_HDR) for t in
              ["Blood Product", "Contents", "Storage", "Shelf Life", "Main Indications", "ABO Compat"]]
    rows = [
        ["pRBC", "Red cells in additive solution", "1–6°C", "42 days",
         "Anemia, acute blood loss,\nsickle cell, hemolysis", "Required (major)"],
        ["FFP", "All coag factors, fibrinogen,\nvWF, protein C/S", "–18°C frozen\nThawed: 1–6°C", "1 year frozen\n24h thawed",
         "Multiple factor deficiency,\nwarfarin reversal (if no PCC),\nMTP, TTP", "Preferred (minor)"],
        ["Cryoprecipitate", "Fibrinogen, vWF,\nFactor VIII, XIII", "–18°C frozen", "1 year frozen\n6h pooled thawed",
         "Hypofibrinogenemia (<150 mg/dL)\nOB hemorrhage, DIC, cardiac surgery", "Not required (adults)"],
        ["Platelets", "Platelets in plasma or\nadditive solution", "20–24°C\n(agitated)", "5 days",
         "Plt <10K (prophylactic)\nPlt <50K (procedure)\nActive bleeding with thrombocytopenia", "Preferred"],
        ["Granulocytes", "WBC concentrate", "20–24°C", "24 hours",
         "Severe neutropenia with\nlife-threatening infection", "Required;\nMUST irradiate"],
    ]
    data = [header]
    for row in rows:
        data.append([Paragraph(str(c), TABLE_CELL) for c in row])
    tbl = Table(data, colWidths=[28*mm, 38*mm, 24*mm, 22*mm, 47*mm, 25*mm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), TEAL),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_TEAL, WHITE, LIGHT_TEAL, WHITE, LIGHT_TEAL]),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
        ('LEFTPADDING', (0, 0), (-1, -1), 4),
        ('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
    ]))
    return tbl


def build_reactions_table():
    header = [Paragraph(t, TABLE_HDR) for t in
              ["Reaction", "Timing", "Key Features", "Risk/Unit", "Management"]]
    rows = [
        ["Febrile Nonhemolytic\n(FNHTR)", "During / ≤4h",
         "Fever ≥1°C rise\nChills, rigors\nNO hemolysis",
         "~1:1,100", "Stop Tx → rule out hemolysis\nAntipyretics → restart if OK\nPrevent: leukoreduction"],
        ["Allergic (mild)", "During / ≤4h",
         "Urticaria, pruritus\nFlushing",
         "~1:100–1,200", "Antihistamine\nMay restart (slower rate)\nPrevent: antihistamine premedication"],
        ["Anaphylaxis\n(IgA deficient)", "During / ≤4h",
         "Hypotension, stridor\nBronchospasm, shock",
         "~1:20,000–50,000", "STOP. Epinephrine\nH1-blocker, corticosteroids\nIgA-deficient or washed units"],
        ["Acute Hemolytic\n(AHTR)", "During / ≤24h",
         "Fever, back pain\nHemoglobinuria\nHypotension, DIC",
         "~1:6,000–110,000\nFatal: ~1:100,000", "STOP immediately\nSaline, maintain urine output\nTreat DIC\nRepeat ABO/DAT\nFDA report"],
        ["TRALI", "≤6h post-Tx",
         "Hypoxia, bilateral CXR\ninfiltrates\nNO hypertension",
         "~1:5,000–140,000", "Supportive, O2, ventilate\nNO diuretics\nUsually resolves 48–96h"],
        ["TACO", "During / ≤12h",
         "Hypertension\nDyspnea, orthopnea\nElevated BNP",
         "~1:9,000", "Slow/stop Tx, sit upright\nO2, furosemide\nBNP/NT-proBNP confirms"],
        ["Delayed Hemolytic\n(DHTR)", "5–14 days",
         "Unexpected Hb fall\nMixed-field DAT\nBilirubinemia",
         "~1:3,000–32,000", "Identify antibody\nAntigen-negative units\nUsually self-limiting"],
        ["Bacterial Sepsis", "During / ≤1h",
         "High fever, rigors\nHypotension, shock",
         "Plt: ~1:25,000\nRBC: ~1:250,000", "STOP, blood cultures\nBroad-spectrum antibiotics\nICU support"],
        ["TA-GvHD", "8–10 days",
         "Fever, rash, diarrhea\nPancytopenia (BM failure)",
         "<1:10,000,000\nFatality ~90%", "Supportive (no effective Tx)\nPREVENT with irradiation"],
    ]
    data = [header]
    bgs = []
    for row in rows:
        data.append([Paragraph(str(c), TABLE_CELL) for c in row])
    
    row_bgs = [LIGHT_BLUE, WHITE, LIGHT_RED, colors.HexColor("#FDEDEC"),
               LIGHT_ORANGE, LIGHT_TEAL, LIGHT_BLUE, colors.HexColor("#FDEDEC"), LIGHT_ORANGE]
    
    tbl = Table(data, colWidths=[32*mm, 20*mm, 44*mm, 28*mm, 54*mm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), MED_RED),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, WHITE, LIGHT_RED, colors.HexColor("#FDEDEC"),
                                               LIGHT_ORANGE, LIGHT_TEAL, LIGHT_BLUE,
                                               colors.HexColor("#FDEDEC"), LIGHT_ORANGE]),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
        ('LEFTPADDING', (0, 0), (-1, -1), 4),
        ('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
        ('FONTNAME', (0, 4), (0, 5), 'Helvetica-Bold'),
    ]))
    return tbl


def build_thresholds_table():
    header = [Paragraph(t, TABLE_HDR) for t in
              ["Patient Population", "Hb Threshold (g/dL)", "Notes"]]
    rows = [
        ["Stable, non-cardiac ICU", "< 7 g/dL", "Restrictive strategy; lower mortality vs liberal"],
        ["Stable surgical patients", "< 7–8 g/dL", "Permit Hb 7-8 in most elective surgery"],
        ["GI bleeding (no CAD)", "< 8 g/dL", "Restrictive reduces rebleeding & mortality"],
        ["Portal HTN bleeding", "7–8 g/dL", "Avoid overtransfusion — worsens portal pressure"],
        ["Known/symptomatic CAD", "> 9 g/dL target", "Liberal threshold; cardiac ischemia must resolve"],
        ["Symptomatic anemia (any)", "Based on symptoms", "Dyspnea, angina, presyncope = transfuse"],
        ["Platelets - prophylaxis", "< 10,000/µL", "< 20K with fever; < 50K before procedure"],
        ["Neurosurgery / eye Sx", "Plt < 100,000/µL", "Higher platelet threshold"],
        ["FFP (active bleeding)", "INR > 1.5–2.0", "Only with active bleeding; not for routine correction"],
        ["Cryoprecipitate", "Fibrinogen < 150–200 mg/dL", "With active bleeding; obstetric threshold may be higher"],
    ]
    data = [header]
    for row in rows:
        data.append([Paragraph(str(c), TABLE_CELL_BOLD if 'Plt' in row[0] or row[0].startswith('Stable') else TABLE_CELL) for c in row])
    
    tbl = Table(data, colWidths=[55*mm, 40*mm, 89*mm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, WHITE] * 6),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
        ('LEFTPADDING', (0, 0), (-1, -1), 5),
        ('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
        ('BACKGROUND', (0, 7), (-1, 7), colors.HexColor("#D5EFF7")),
        ('BACKGROUND', (0, 8), (-1, 8), colors.HexColor("#FEF9E7")),
        ('BACKGROUND', (0, 9), (-1, 9), colors.HexColor("#FEF9E7")),
    ]))
    return tbl


def build_modifications_table():
    header = [Paragraph(t, TABLE_HDR) for t in
              ["Modification", "Process", "Prevents / Achieves", "Key Indications"]]
    rows = [
        ["Leukoreduction\n(Leukodepleted)", "Filter removes WBCs\n(standard in most blood banks)",
         "Prevents FNHTR, reduces CMV\ntransmission, reduces HLA\nalloimmunization",
         "All cellular products (standard)\nHLA-sensitized patients\nChronic transfusion"],
        ["Irradiation\n(Gamma / X-ray)", "Inactivates donor T-lymphocytes",
         "Prevents TA-GvHD\n(fatality ~90%)",
         "Immunocompromised (HSCT, congenital\nimmunodeficiency), directed (family)\ndonations, HLA-matched platelets,\nfludarabine/cladribine therapy"],
        ["CMV-Negative\n(Seronegative)", "Donors tested CMV IgG negative",
         "Prevents CMV transmission in\nhigh-risk patients",
         "CMV-neg immunocompromised patients\nCMV-neg pregnant women\n(Leukoreduced blood is CMV-safe equivalent)"],
        ["Washed RBC/Plt", "Saline washing removes\nplasma proteins",
         "Removes IgA, allergens\nReduces allergic reactions",
         "IgA-deficient patients with anti-IgA\nSevere recurrent allergic reactions"],
        ["Irradiated +\nLeukoreduced", "Both modifications applied",
         "Prevents both TA-GvHD\nand CMV/FNHTR",
         "All immunocompromised patients\nIntrauterine transfusions (IUT)"],
    ]
    data = [header]
    for row in rows:
        data.append([Paragraph(str(c), TABLE_CELL) for c in row])
    
    tbl = Table(data, colWidths=[33*mm, 42*mm, 45*mm, 64*mm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#6C3483")),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.HexColor("#F5EEF8"), WHITE,
                                               colors.HexColor("#F5EEF8"), WHITE,
                                               colors.HexColor("#EAF2FF")]),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
        ('LEFTPADDING', (0, 0), (-1, -1), 5),
        ('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
    ]))
    return tbl


# ─── Cover Page ───────────────────────────────────────────────────────────────
class CoverPage(Flowable):
    def __init__(self):
        super().__init__()
        self.width = W - 30*mm
        self.height = 120*mm

    def draw(self):
        c = self.canv
        # Background gradient simulation
        c.setFillColor(DARK_BLUE)
        c.roundRect(0, 0, self.width, self.height, 8*mm, fill=1, stroke=0)
        # Decorative red strip
        c.setFillColor(MED_RED)
        c.rect(0, self.height - 18*mm, self.width, 18*mm, fill=1, stroke=0)
        # Red circle decoration
        c.setFillColor(colors.HexColor("#3D6B99"))
        c.circle(self.width - 20*mm, 20*mm, 30*mm, fill=1, stroke=0)
        c.setFillColor(colors.HexColor("#2A5580"))
        c.circle(self.width - 15*mm, 15*mm, 18*mm, fill=1, stroke=0)
        # Gold accent line
        c.setFillColor(GOLD)
        c.rect(12*mm, self.height - 18*mm - 3*mm, self.width - 24*mm, 3*mm, fill=1, stroke=0)
        # Title
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 22)
        c.drawString(14*mm, self.height - 13*mm, "Blood Transfusion Protocols")
        c.setFont('Helvetica-Bold', 16)
        c.setFillColor(GOLD)
        c.drawString(14*mm, self.height - 38*mm, "Clinical Reference & Reaction Flowcharts")
        c.setFillColor(colors.HexColor("#AED6F1"))
        c.setFont('Helvetica', 10)
        c.drawString(14*mm, self.height - 52*mm, "Blood Products  |  Transfusion Thresholds  |  ABO Compatibility")
        c.drawString(14*mm, self.height - 63*mm, "Transfusion Reactions  |  Massive Transfusion Protocol  |  Special Modifications")
        # Bottom
        c.setFillColor(colors.HexColor("#AED6F1"))
        c.setFont('Helvetica', 9)
        c.drawString(14*mm, 12*mm, "Based on: Tietz Lab Medicine 7e | Goldman-Cecil Medicine | Miller's Anesthesia 10e | Quick Compendium 5e")
        c.setFillColor(GOLD)
        c.drawRightString(self.width - 8*mm, 12*mm, "July 2026")

    def wrap(self, avW, avH):
        return self.width, self.height


# ─── Build Document ───────────────────────────────────────────────────────────
def build_pdf():
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        leftMargin=15*mm, rightMargin=15*mm,
        topMargin=15*mm, bottomMargin=22*mm,
        title="Blood Transfusion Protocols & Reference",
        author="Orris Medical Assistant",
        subject="Blood Transfusion Clinical Reference"
    )

    story = []
    S = Spacer

    # ── Cover ──
    story.append(CoverPage())
    story.append(S(1, 8*mm))

    # TOC-like summary box
    toc_data = [
        [Paragraph("📋  Contents", ParagraphStyle('toc_hdr', fontName='Helvetica-Bold',
                   fontSize=10, textColor=DARK_BLUE, leading=14))],
        [Paragraph(
            "<font color='#2E86C1'>1.</font> Blood Products &amp; Storage  &nbsp;&nbsp;"
            "<font color='#2E86C1'>2.</font> ABO/Rh Compatibility  &nbsp;&nbsp;"
            "<font color='#2E86C1'>3.</font> Transfusion Thresholds<br/>"
            "<font color='#2E86C1'>4.</font> pRBC Decision Flowchart  &nbsp;&nbsp;"
            "<font color='#2E86C1'>5.</font> Transfusion Reaction Flowchart  &nbsp;&nbsp;"
            "<font color='#2E86C1'>6.</font> Reaction Reference Table<br/>"
            "<font color='#2E86C1'>7.</font> Massive Transfusion Protocol  &nbsp;&nbsp;"
            "<font color='#2E86C1'>8.</font> Special Blood Modifications  &nbsp;&nbsp;"
            "<font color='#2E86C1'>9.</font> Key Tests &amp; Concepts",
            ParagraphStyle('toc_body', fontName='Helvetica', fontSize=8.5,
                           textColor=DARK_GREY, leading=14)
        )]
    ]
    toc_tbl = Table(toc_data, colWidths=[W - 30*mm])
    toc_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), LIGHT_BLUE),
        ('BOX', (0, 0), (-1, -1), 1.5, MED_BLUE),
        ('TOPPADDING', (0, 0), (-1, -1), 6),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
        ('LEFTPADDING', (0, 0), (-1, -1), 10),
    ]))
    story.append(toc_tbl)
    story.append(PageBreak())

    # ── Section 1: Blood Products ──
    story.append(SectionHeader("1  Blood Products & Storage", TEAL))
    story.append(S(1, 3*mm))
    story.append(build_products_table())
    story.append(S(1, 3*mm))
    story.append(Paragraph(
        "<b>Key Points:</b> Each pRBC unit raises Hb by ~1 g/dL in an average adult. Platelets are stored at room temperature "
        "on an agitator — highest bacterial contamination risk. FFP must be ABO compatible (plasma contains antibodies). "
        "Cryoprecipitate is the preferred fibrinogen source; NOT interchangeable with FFP.",
        BODY_STYLE))
    story.append(S(1, 5*mm))

    # ── Section 2: ABO Compatibility ──
    story.append(SectionHeader("2  ABO & Rh Compatibility", DARK_BLUE))
    story.append(S(1, 3*mm))
    story.append(build_abo_table())
    story.append(S(1, 3*mm))
    story.append(BoxedNote(
        "EMERGENCY: Group O pRBCs (O-negative for females of childbearing potential) + Group AB plasma before type known",
        bg=LIGHT_BLUE, border=MED_BLUE, icon="E"))
    story.append(S(1, 3*mm))
    story.append(Paragraph(
        "<b>Rh(D):</b> D-negative females of childbearing age MUST receive D-negative pRBCs to prevent anti-D formation "
        "(risk ~10–20% per D+ unit), which can cause haemolytic disease of the fetus/newborn (HDN). "
        "Males and post-menopausal females can receive D+ components if D- is scarce. "
        "<b>Group A plasma</b> is increasingly used in MTP due to scarcity of AB (only 4% of population).",
        BODY_STYLE))
    story.append(S(1, 5*mm))

    # ── Section 3: Thresholds ──
    story.append(SectionHeader("3  Evidence-Based Transfusion Thresholds", DARK_BLUE))
    story.append(S(1, 3*mm))
    story.append(build_thresholds_table())
    story.append(S(1, 3*mm))
    story.append(Paragraph(
        "<b>Restrictive strategy (Hb threshold 7–8 g/dL)</b> is associated with lower all-cause mortality and rebleeding "
        "rates in RCTs for GI bleeding. <b>One-unit-at-a-time</b> transfusion with reassessment is recommended. "
        "Healthy volunteers can tolerate Hb as low as 5.1 g/dL short-term.",
        BODY_STYLE))
    story.append(PageBreak())

    # ── Section 4: pRBC Decision Flowchart ──
    story.append(SectionHeader("4  pRBC Transfusion Decision Flowchart", DARK_BLUE))
    story.append(S(1, 4*mm))
    story.append(build_prbc_flowchart())
    story.append(S(1, 2*mm))
    story.append(Paragraph(
        "Fig. 1 - pRBC transfusion decision algorithm. In life-threatening hemorrhage, use uncrossmatched "
        "group O blood while type and screen is being performed. For stable patients, base decision on Hb thresholds above.",
        CAPTION))
    story.append(S(1, 5*mm))

    # Pretransfusion testing box
    story.append(SectionHeader("5  Pretransfusion Testing", TEAL))
    story.append(S(1, 3*mm))
    pre_data = [
        [Paragraph("Test", TABLE_HDR), Paragraph("What It Does", TABLE_HDR),
         Paragraph("When Valid", TABLE_HDR), Paragraph("Key Point", TABLE_HDR)],
        [Paragraph("Type & Screen\n(T&S)", TABLE_CELL_BOLD),
         Paragraph("ABO/D typing + antibody screen\n(unexpected alloantibodies)", TABLE_CELL),
         Paragraph("No recent Tx/pregnancy:\nup to 1 month\nRecent Tx/pregnancy:\n3 days only", TABLE_CELL),
         Paragraph("Most hemolytic reactions = clerical/\nID errors, not testing failures", TABLE_CELL)],
        [Paragraph("Type & Crossmatch", TABLE_CELL_BOLD),
         Paragraph("T&S + select and reserve\nspecific RBC units for patient", TABLE_CELL),
         Paragraph("Same as T&S", TABLE_CELL),
         Paragraph("Electronic crossmatch if 2 concordant\nABO types & negative antibody screen", TABLE_CELL)],
        [Paragraph("DAT\n(Direct Coombs)", TABLE_CELL_BOLD),
         Paragraph("Detects Ab/complement bound\nto patient's own RBCs", TABLE_CELL),
         Paragraph("Any time", TABLE_CELL),
         Paragraph("Positive in: AIHA, AHTR, HDN,\ndrug-induced hemolysis", TABLE_CELL)],
        [Paragraph("IAT\n(Indirect Coombs)", TABLE_CELL_BOLD),
         Paragraph("Detects free antibodies in\npatient serum vs donor RBCs", TABLE_CELL),
         Paragraph("Any time", TABLE_CELL),
         Paragraph("Used for antibody screening,\nidentification, and crossmatch", TABLE_CELL)],
    ]
    pre_tbl = Table(pre_data, colWidths=[30*mm, 52*mm, 38*mm, 64*mm])
    pre_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), TEAL),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_TEAL, WHITE, LIGHT_TEAL, WHITE]),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
        ('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(pre_tbl)
    story.append(PageBreak())

    # ── Section 6: Reaction Flowchart ──
    story.append(SectionHeader("6  Transfusion Reaction Management Flowchart", MED_RED))
    story.append(S(1, 4*mm))
    story.append(build_reaction_flowchart())
    story.append(S(1, 2*mm))
    story.append(Paragraph(
        "Fig. 2 - Algorithm for managing suspected transfusion reactions. "
        "TRALI = non-cardiogenic pulmonary edema (no diuretics). TACO = cardiogenic overload (give diuretics). "
        "Key distinction: TACO has hypertension + elevated BNP; TRALI has hypotension/normotension, no BNP rise.",
        CAPTION))
    story.append(PageBreak())

    # ── Section 7: Reactions Table ──
    story.append(SectionHeader("7  Transfusion Reactions Reference Table", MED_RED))
    story.append(S(1, 3*mm))
    story.append(build_reactions_table())
    story.append(S(1, 3*mm))
    story.append(Paragraph(
        "<b>Most common reaction:</b> FNHTR (~1:1,100). "
        "<b>Most dangerous acute reaction:</b> AHTR (usually ABO incompatibility from clerical error). "
        "<b>Leading cause of transfusion death (USA):</b> TRALI. "
        "<b>Highest preventable mortality:</b> TA-GvHD (prevent with irradiation).",
        BODY_STYLE))
    story.append(PageBreak())

    # ── Section 8: MTP ──
    story.append(SectionHeader("8  Massive Transfusion Protocol (MTP)", MED_RED))
    story.append(S(1, 4*mm))
    story.append(build_mtp_flowchart())
    story.append(S(1, 3*mm))
    story.append(Paragraph(
        "Fig. 3 - Massive Transfusion Protocol overview. The 1:1:1 ratio (FFP:Plt:pRBC) is supported by the "
        "PROPPR trial. Communicate clearly with blood bank — activate and deactivate MTP explicitly. "
        "Monitor: iCa²⁺ (citrate chelation), K⁺ (stored RBC), temperature (use blood warmers), and coagulation.",
        CAPTION))
    story.append(S(1, 5*mm))

    # Lethal triad box
    triad_data = [
        [Paragraph("THE LETHAL TRIAD OF MASSIVE HEMORRHAGE", ParagraphStyle('triad_hdr',
          fontName='Helvetica-Bold', fontSize=10, textColor=WHITE, alignment=TA_CENTER))],
        [Table([[
            Paragraph("HYPOTHERMIA\n< 35°C\n→ Use blood warmers\nwarm IV fluids\nBair Hugger blankets",
                       ParagraphStyle('triad', fontName='Helvetica-Bold', fontSize=8.5,
                                      textColor=DARK_BLUE, alignment=TA_CENTER, leading=13)),
            Paragraph("ACIDOSIS\npH < 7.35\n→ Balanced MTP\nBuffer with bicarb\nif persistent",
                       ParagraphStyle('triad', fontName='Helvetica-Bold', fontSize=8.5,
                                      textColor=DARK_BLUE, alignment=TA_CENTER, leading=13)),
            Paragraph("COAGULOPATHY\nDilutional / consumptive\n→ Balanced ratio\nAvoid pRBC-only\nCheck TEG/ROTEM",
                       ParagraphStyle('triad', fontName='Helvetica-Bold', fontSize=8.5,
                                      textColor=DARK_BLUE, alignment=TA_CENTER, leading=13)),
        ], colWidths=[52*mm, 52*mm, 52*mm])],
    ]
    triad_tbl = Table(triad_data, colWidths=[W - 30*mm])
    triad_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (0, 0), MED_RED),
        ('BACKGROUND', (0, 1), (0, 1), LIGHT_ORANGE),
        ('BOX', (0, 0), (-1, -1), 1.5, MED_RED),
        ('TOPPADDING', (0, 0), (-1, -1), 6),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
        ('LEFTPADDING', (0, 0), (-1, -1), 6),
    ]))
    story.append(triad_tbl)
    story.append(PageBreak())

    # ── Section 9: Special Modifications ──
    story.append(SectionHeader("9  Special Blood Product Modifications", colors.HexColor("#6C3483")))
    story.append(S(1, 3*mm))
    story.append(build_modifications_table())
    story.append(S(1, 5*mm))

    # ── Section 10: Key Tests & Concepts ──
    story.append(SectionHeader("10  Key Blood Bank Concepts for Clinicians", DARK_BLUE))
    story.append(S(1, 3*mm))

    concepts = [
        ("Platelet Refractoriness",
         "Failure to achieve expected platelet increment. Check CCI at 1 hour post-transfusion: "
         "CCI < 5,000–7,500 = immune cause (HLA alloantibodies most common). "
         "CCI normal at 1h but drops later = non-immune (DIC, sepsis, splenomegaly). "
         "Immune: provide HLA-matched apheresis platelets."),
        ("Delayed Serologic Transfusion Reaction (DSTR)",
         "New alloantibody detected after transfusion WITHOUT hemolysis. More common than DHTR (3:1 ratio). "
         "Most commonly implicated antigens: Kidd (Jk), E, c (Rh), Kell, Duffy. "
         "Kidd antibodies can cause severe intravascular hemolysis."),
        ("IgA Deficiency & Anaphylaxis",
         "Affects 1 in 700 people. Patients with anti-IgA antibodies are at risk for anaphylaxis to blood products. "
         "Test for IgA deficiency in any patient with severe allergic transfusion reaction. "
         "Management: IgA-deficient donor units OR washed products."),
        ("Autologous Transfusion Options",
         "1) Preoperative autologous donation (PAD): donate weeks before elective surgery. "
         "2) Acute normovolemic hemodilution (ANH): remove blood at surgery start, replace with crystalloid, reinfuse later. "
         "3) Cell salvage: contraindicated in cancer surgery and infected fields."),
        ("Transfusion in Jehovah's Witnesses",
         "Refuse allogeneic blood on religious grounds. Alternatives: cell salvage (if accepted in closed circuit), "
         "EPO + iron preoperatively, fibrin glue, minimize iatrogenic blood loss. "
         "Always obtain informed refusal. Ethics committee for unconscious patients."),
        ("Hyperhemolysis in Sickle Cell Disease",
         "Rare but life-threatening: both donor AND patient's own cells are destroyed post-transfusion. "
         "Paradox: further transfusion may worsen hemolysis. Use extended phenotype-matched RBCs. "
         "Exchange transfusion (apheresis) preferred for stroke prevention/treatment."),
    ]

    for title, text in concepts:
        cdata = [[
            Paragraph(title, TABLE_CELL_BOLD),
            Paragraph(text, TABLE_CELL),
        ]]
        ctbl = Table(cdata, colWidths=[45*mm, 139*mm])
        ctbl.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (0, 0), LIGHT_BLUE),
            ('BACKGROUND', (1, 0), (1, 0), WHITE),
            ('BOX', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
            ('INNERGRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
            ('TOPPADDING', (0, 0), (-1, -1), 5),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
            ('LEFTPADDING', (0, 0), (-1, -1), 6),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ]))
        story.append(ctbl)
        story.append(S(1, 1*mm))

    story.append(S(1, 6*mm))

    # ── Final Reference Box ──
    final_data = [[
        Paragraph(
            "<b>Sources &amp; References</b><br/>"
            "Tietz Textbook of Laboratory Medicine, 7th Edition (Chapters 92, 93) &nbsp;|&nbsp; "
            "Goldman-Cecil Medicine, International Edition (Chapter 162) &nbsp;|&nbsp; "
            "Quick Compendium of Clinical Pathology, 5th Edition (Chapter 2.8) &nbsp;|&nbsp; "
            "Miller's Anesthesia, 10th Edition (Chapter 45) &nbsp;|&nbsp; "
            "Rosen's Emergency Medicine, 10th Edition",
            ParagraphStyle('refs', fontName='Helvetica', fontSize=7.5, textColor=MID_GREY,
                            leading=12, alignment=TA_CENTER)
        )
    ]]
    final_tbl = Table(final_data, colWidths=[W - 30*mm])
    final_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), LIGHT_GREY),
        ('BOX', (0, 0), (-1, -1), 1, MID_GREY),
        ('TOPPADDING', (0, 0), (-1, -1), 8),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
        ('LEFTPADDING', (0, 0), (-1, -1), 10),
    ]))
    story.append(final_tbl)

    doc.build(story, onFirstPage=on_first_page, onLaterPages=on_page)
    print(f"PDF created: {OUTPUT_PATH}")
    return OUTPUT_PATH


if __name__ == '__main__':
    build_pdf()
Running Command

cd /tmp/workspace/blood-transfusion-pdf && python3 generate_pdf.py 2>&1

Reading File
Editing File

~/blood-transfusion-pdf/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/blood-transfusion-pdf/generate_pdf.py'
Running Command

cd /tmp/workspace/blood-transfusion-pdf && python3 generate_pdf.py 2>&1

Editing File

~/blood-transfusion-pdf/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/blood-transfusion-pdf/generate_pdf.py'
Running Command

cd /tmp/workspace/blood-transfusion-pdf && python3 generate_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/blood-transfusion-pdf/Blood_Transfusion_Protocols.pdf

Your PDF is ready - 30 KB, 8 pages, A4 format.

Blood Transfusion Protocols

PDF Document · PDF


What's inside the PDF:
SectionContent
CoverTitle page with color design and source references
1 - Blood Products & StorageFull table: pRBC, FFP, Cryo, Platelets, Granulocytes - contents, storage, shelf life, indications, ABO compatibility
2 - ABO/Rh CompatibilityComplete compatibility matrix for pRBCs and plasma; Rh rules; emergency transfusion guide
3 - Transfusion ThresholdsEvidence-based Hb triggers by patient population; platelet/FFP/cryo thresholds
4 - pRBC Decision FlowchartStep-by-step algorithm: emergency vs stable, T&S, Hb threshold branches
5 - Pretransfusion TestingT&S, crossmatch, DAT, IAT - what each test does, validity period, key points
6 - Reaction Management FlowchartFull color coded algorithm: STOP → clerical check → labs → hemolysis? → TRALI vs TACO branches; FDA reporting rule
7 - Reactions Reference TableAll 9 major reactions with timing, features, risk per unit, and management
8 - Massive Transfusion ProtocolMTP trigger, 1:1:1 pack composition, Lethal Triad box (hypothermia/acidosis/coagulopathy)
9 - Special ModificationsLeukoreduction, irradiation, CMV-negative, washed - indications for each
10 - Key ConceptsPlatelet refractoriness, DHTR/DSTR, IgA deficiency, autologous options, Jehovah's Witnesses, hyperhemolysis in SCD
This is a shared conversation. Sign in to Orris to start your own chat.