Cbc

Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Finding Sources
Reading File
Finding Sources
Reading File
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Loading Image
Here is a thorough overview of the Complete Blood Count (CBC) based on authoritative medical textbooks.

Complete Blood Count (CBC)

The CBC measures the three major cellular components of blood: red blood cells (RBCs), white blood cells (WBCs), and platelets. It uses automated electronic cell counters that count, size, and characterize cells, providing a five-part WBC differential and indices like MCV and RDW.
Blood composition diagram showing plasma, buffy coat, and erythrocyte layers with normal reference ranges

CBC Components and Normal Reference Ranges

Red Blood Cell Parameters

TestDescriptionNormal Range (Male)Normal Range (Female)
Hemoglobin (Hb)O2-carrying capacity of blood13.5-17.5 g/dL12.0-16.0 g/dL
Hematocrit (Hct)% of whole blood volume occupied by RBCs39-49%35-45%
RBC countErythrocyte count4.3-5.7 × 10⁶ cells/µL3.8-5.1 × 10⁶ cells/µL
MCV (Mean Corpuscular Volume)Average RBC size80-100 fL80-100 fL
MCH (Mean Corpuscular Hgb)Hb per RBC (Hb ÷ RBC)27-33 pg27-33 pg
MCHC (Mean Corpuscular Hgb Concentration)Hb concentration per volume of RBCs33-36 g/dL33-36 g/dL
RDW (Red Cell Distribution Width)Variation in RBC size (anisocytosis)11.5-14.5%11.5-14.5%
Reticulocyte countImmature RBCs; reflects bone marrow activity0.5-1.5% of RBCs

White Blood Cell Parameters

TestNormal Range
Total WBC count4,500-11,000 cells/mm³
Neutrophils (segs + bands)50-70% (absolute: 1,800-7,000/µL)
Lymphocytes20-40%
Monocytes2-8%
Eosinophils1-4%
Basophils0.5-1%

Platelet Parameters

TestNormal Range
Platelet count150,000-400,000/mm³
MPV (Mean Platelet Volume)7.5-12.5 fL

Classifying Anemia by MCV

The first step when anemia is found on a CBC is to classify it by MCV:
  • Microcytic (MCV < 80 fL): Think iron deficiency, thalassemia, anemia of chronic disease. Check ferritin - if < 30 ng/mL, confirms iron deficiency.
  • Normocytic (MCV 80-100 fL): Think acute blood loss, hemolysis, renal insufficiency, early mixed deficiency. In early anemia, MCV may change before Hb/Hct.
  • Macrocytic (MCV > 100 fL): Think B12/folate deficiency, alcohol, hypothyroidism, medications (hydroxyurea, methotrexate).

WBC Differential - Key Abnormalities

FindingDefinitionCommon Causes
LeukocytosisWBC > 10,000/mm³Infection, inflammation, leukemia, steroids, stress
LeukopeniaWBC < 4,500/mm³Viral infection, drugs, autoimmune, B12/folate deficiency
NeutrophiliaANC > 7,000/µLBacterial infection, steroids, stress, burns, MI
NeutropeniaANC < 1,800/µL (severe: <500)Drug reaction, viral infection, aplastic anemia
LymphocytosisLymphs > 4,000/µL (adult)Viral infections (EBV, CMV), CLL, pertussis
LymphopeniaLymphs < 1,500/µL (adult)HIV, steroids, radiation, immunosuppressants
EosinophiliaEosinophils > 500/µLParasites, atopy/asthma, drug reactions, vasculitis
MonocytosisMonocytes > 800/µLChronic infection (TB), inflammatory disease, malignancy

Platelet Abnormalities

  • Thrombocytopenia (platelets < 150,000/mm³): Causes include decreased production (B12/folate deficiency, aplastic anemia, chemotherapy, leukemia, alcohol, viral infections including HIV) or increased destruction (immune thrombocytopenia [ITP], DIC, HUS, heparin-induced [HIT], HELLP syndrome, sepsis, splenic sequestration).
  • Thrombocytosis (platelets > 400,000/mm³): Reactive (iron deficiency, infection, inflammation, post-splenectomy) or primary (essential thrombocythemia).

Peripheral Blood Smear

Manual smear review is performed when automated analyzers flag abnormalities. Indications include:
  • Suspected hemolysis
  • RBC inclusions (Howell-Jolly bodies, Heinz bodies, malaria)
  • Myelodysplasia or megaloblastic changes
  • Unexplained thrombocytopenia/thrombocytosis
  • Immature or abnormal WBCs (blasts)

Clinical Use of CBC

The CBC is ordered for:
  • Screening for anemia, infection, or clotting disorders
  • Monitoring bone marrow response to treatment (e.g., chemotherapy)
  • Evaluating unexplained fatigue, fever, bruising, or bleeding
  • Surveillance in chronic disease (CKD, HIV, autoimmune diseases)
  • Pre-operative assessment
The absolute cell counts (not just percentages) are most clinically meaningful - calculate the absolute neutrophil count (ANC) = Total WBC × (% neutrophils + % bands).

Sources: Textbook of Family Medicine, 9e (eTable 14-4); Henry's Clinical Diagnosis and Management by Laboratory Methods; Junqueira's Basic Histology, 17e

Create a quick reference PDF guide for CBC interpretation

Reading File
Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace not found"

Running Command

mkdir -p /home/daytona/workspace/cbc-guide

Writing File

~/cbc-guide/generate_cbc_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.lib.colors import HexColor
import os

OUTPUT = "/home/daytona/workspace/cbc-guide/CBC_Quick_Reference.pdf"

# --- Colour palette ---
DEEP_BLUE   = HexColor("#1A3C5E")
MED_BLUE    = HexColor("#2E6DA4")
LIGHT_BLUE  = HexColor("#D6E8F7")
RED_COL     = HexColor("#C0392B")
ORANGE_COL  = HexColor("#E67E22")
GREEN_COL   = HexColor("#27AE60")
GREY_BG     = HexColor("#F4F6F8")
GREY_LINE   = HexColor("#BDC3C7")
WHITE       = colors.white
BLACK       = colors.black

# --- Document ---
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=15*mm, rightMargin=15*mm,
    topMargin=15*mm, bottomMargin=15*mm,
    title="CBC Quick Reference Guide",
    author="Orris Medical"
)

W, H = A4
CONTENT_W = W - 30*mm

styles = getSampleStyleSheet()

def style(name, **kw):
    base = styles[name] if name in styles else styles["Normal"]
    s = ParagraphStyle(name + "_custom", parent=base, **kw)
    return s

# Custom styles
TITLE_S   = style("Title",   fontSize=22, textColor=WHITE,
                  alignment=TA_CENTER, spaceAfter=2, fontName="Helvetica-Bold")
SUBTITLE_S= style("Normal",  fontSize=10, textColor=HexColor("#AED6F1"),
                  alignment=TA_CENTER, spaceAfter=0, fontName="Helvetica")
SEC_S     = style("Heading1",fontSize=11, textColor=WHITE,
                  fontName="Helvetica-Bold", spaceBefore=0, spaceAfter=0,
                  alignment=TA_LEFT)
BODY_S    = style("Normal",  fontSize=8.5, textColor=BLACK,
                  fontName="Helvetica", leading=12, spaceAfter=2)
BOLD_S    = style("Normal",  fontSize=8.5, textColor=DEEP_BLUE,
                  fontName="Helvetica-Bold", leading=12)
SMALL_S   = style("Normal",  fontSize=7.5, textColor=HexColor("#555555"),
                  fontName="Helvetica", leading=11)
CELL_HDR  = style("Normal",  fontSize=8, textColor=WHITE,
                  fontName="Helvetica-Bold", alignment=TA_CENTER, leading=10)
CELL_S    = style("Normal",  fontSize=8, textColor=BLACK,
                  fontName="Helvetica", leading=10)
CELL_B    = style("Normal",  fontSize=8, textColor=DEEP_BLUE,
                  fontName="Helvetica-Bold", leading=10)
NOTE_S    = style("Normal",  fontSize=7.5, textColor=HexColor("#7F8C8D"),
                  fontName="Helvetica-Oblique", leading=10)
FORMULA_S = style("Normal",  fontSize=8, textColor=DEEP_BLUE,
                  fontName="Helvetica-Bold", leading=11, alignment=TA_CENTER)


# ============================================================
# HELPER: section banner
# ============================================================
def section_banner(title, color=MED_BLUE):
    tbl = Table([[Paragraph(title, SEC_S)]], colWidths=[CONTENT_W])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), 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),
        ("ROUNDEDCORNERS", [4]),
    ]))
    return tbl


# ============================================================
# TITLE BLOCK
# ============================================================
def title_block():
    tbl = Table(
        [[Paragraph("CBC Quick Reference Guide", TITLE_S)],
         [Paragraph("Complete Blood Count • Normal Values • Interpretation • Clinical Pearls", SUBTITLE_S)]],
        colWidths=[CONTENT_W]
    )
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), DEEP_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 10),
        ("BOTTOMPADDING", (0,0), (-1,-1), 10),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("ROUNDEDCORNERS", [6]),
    ]))
    return tbl


# ============================================================
# 1. RBC PARAMETERS TABLE
# ============================================================
def rbc_table():
    headers = ["Parameter", "Description", "Male", "Female", "SI Units"]
    data = [
        ["Hemoglobin (Hb)", "O2-carrying capacity", "13.5-17.5 g/dL", "12.0-16.0 g/dL", "135-175 / 120-160 g/L"],
        ["Hematocrit (Hct)", "% volume of RBCs in blood", "39-49%", "35-45%", "0.39-0.49 / 0.35-0.45"],
        ["RBC Count", "Erythrocyte count", "4.3-5.7 x10\u2076/µL", "3.8-5.1 x10\u2076/µL", "x10\u00b9\u00b2/L"],
        ["MCV", "Mean cell volume (size)", "80-100 fL", "80-100 fL", "Same"],
        ["MCH", "Mean cell Hb (Hb / RBC)", "27-33 pg", "27-33 pg", "Same"],
        ["MCHC", "Mean cell Hb concentration", "33-36 g/dL", "33-36 g/dL", "330-360 g/L"],
        ["RDW", "Size variation of RBCs", "11.5-14.5%", "11.5-14.5%", "Same"],
        ["Reticulocytes", "Immature RBCs (BM activity)", "0.5-1.5%", "0.5-1.5%", "Same"],
    ]
    rows = [[Paragraph(h, CELL_HDR) for h in headers]]
    for i, row in enumerate(data):
        cells = [Paragraph(row[0], CELL_B)] + [Paragraph(c, CELL_S) for c in row[1:]]
        rows.append(cells)

    col_w = [38*mm, 48*mm, 28*mm, 28*mm, 38*mm]
    tbl = Table(rows, colWidths=col_w, repeatRows=1)
    tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), MED_BLUE),
        ("BACKGROUND",    (0,1), (-1,-1), WHITE),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, GREY_BG]),
        ("GRID",          (0,0), (-1,-1), 0.4, GREY_LINE),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("LINEBELOW",     (0,0), (-1,0), 1, DEEP_BLUE),
    ]))
    return tbl


# ============================================================
# 2. WBC TABLE
# ============================================================
def wbc_table():
    headers = ["Cell Type", "% (Differential)", "Absolute Count", "Key Role"]
    data = [
        ["Neutrophils (segs+bands)", "50-70%", "1,800-7,000/µL", "First responders; bacterial/fungal infection"],
        ["Lymphocytes", "20-40%", "1,000-4,800/µL", "Adaptive immunity; viral defense"],
        ["Monocytes", "2-8%", "200-800/µL", "Phagocytosis; antigen presentation"],
        ["Eosinophils", "1-4%", "45-440/µL", "Parasites; allergic/hypersensitivity reactions"],
        ["Basophils", "0.5-1%", "0-100/µL", "IgE-mediated allergy; heparin release"],
        ["Total WBC", "—", "4,500-11,000/µL", "Overall leukocyte burden"],
    ]
    rows = [[Paragraph(h, CELL_HDR) for h in headers]]
    bg_col = [HexColor("#EAF4FD"), HexColor("#F0FBF4"), HexColor("#FDF6E3"),
              HexColor("#FDF0ED"), HexColor("#F3EDF7"), GREY_BG]
    for i, row in enumerate(data):
        bg = bg_col[i]
        cells = [Paragraph(row[0], CELL_B)] + [Paragraph(c, CELL_S) for c in row[1:]]
        rows.append(cells)

    col_w = [42*mm, 30*mm, 32*mm, 76*mm]
    tbl = Table(rows, colWidths=col_w, repeatRows=1)
    tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), MED_BLUE),
        ("GRID",          (0,0), (-1,-1), 0.4, GREY_LINE),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, GREY_BG]),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("LINEBELOW",     (0,0), (-1,0), 1, DEEP_BLUE),
    ]))
    return tbl


# ============================================================
# 3. ANEMIA CLASSIFICATION
# ============================================================
def anemia_table():
    headers = ["Type", "MCV", "Common Causes", "Key Initial Test"]
    data = [
        ["Microcytic",  "< 80 fL",
         "Iron deficiency, Thalassemia, Anemia of chronic disease (late), Sideroblastic",
         "Serum Ferritin (<30 ng/mL = iron def.)"],
        ["Normocytic",  "80-100 fL",
         "Acute blood loss, Hemolysis, Renal failure (CKD), Early mixed deficiency, Aplastic anemia",
         "Reticulocyte count, BMP, LFTs"],
        ["Macrocytic",  "> 100 fL",
         "B12 / Folate deficiency, Alcohol, Hypothyroidism, Meds (MTX, hydroxyurea, AZT)",
         "B12, Folate, TFTs, peripheral smear"],
    ]
    row_colors = [HexColor("#FDECEA"), HexColor("#EAF4FD"), HexColor("#F0FBF4")]
    rows = [[Paragraph(h, CELL_HDR) for h in headers]]
    for i, row in enumerate(data):
        cells = [Paragraph(row[0], CELL_B),
                 Paragraph(row[1], FORMULA_S),
                 Paragraph(row[2], CELL_S),
                 Paragraph(row[3], CELL_S)]
        rows.append(cells)

    col_w = [25*mm, 18*mm, 72*mm, 65*mm]
    tbl = Table(rows, colWidths=col_w, repeatRows=1)
    tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), MED_BLUE),
        ("BACKGROUND",    (0,1), (1,1), HexColor("#FDECEA")),
        ("BACKGROUND",    (0,2), (1,2), HexColor("#EAF4FD")),
        ("BACKGROUND",    (0,3), (1,3), HexColor("#F0FBF4")),
        ("GRID",          (0,0), (-1,-1), 0.4, GREY_LINE),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [HexColor("#FDECEA"), HexColor("#EAF4FD"), HexColor("#F0FBF4")]),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("LINEBELOW",     (0,0), (-1,0), 1, DEEP_BLUE),
    ]))
    return tbl


# ============================================================
# 4. WBC ABNORMALITIES TABLE
# ============================================================
def wbc_abnorm_table():
    headers = ["Finding", "Definition", "Common Causes"]
    data = [
        ["Leukocytosis",  "WBC > 11,000/µL",
         "Bacterial infection, inflammation, steroids, stress, CML, leukemoid reaction"],
        ["Leukopenia",    "WBC < 4,500/µL",
         "Viral infections, medications, autoimmune, B12/folate deficiency, aplastic anemia"],
        ["Neutrophilia",  "ANC > 7,000/µL",
         "Bacterial infection, trauma, MI, burns, surgery, steroids, myeloproliferative disorders"],
        ["Neutropenia",   "ANC < 1,500/µL (severe <500)",
         "Chemotherapy/radiation, drug reaction (clozapine, carbimazole), viral infection, SLE, aplastic anemia"],
        ["Lymphocytosis", "Lymphs > 4,000/µL (adult)",
         "Viral (EBV/CMV/hepatitis), CLL, ALL, pertussis, toxoplasmosis"],
        ["Lymphopenia",   "Lymphs < 1,500/µL",
         "HIV/AIDS, corticosteroids, immunosuppressants, radiation, autoimmune"],
        ["Eosinophilia",  "Eos > 500/µL",
         "Parasites (NAACP), atopy/asthma, drug hypersensitivity, Addison's, malignancy, vasculitis"],
        ["Monocytosis",   "Monos > 800/µL",
         "Chronic infections (TB, SBE), inflammatory bowel disease, myelomonocytic leukemia"],
        ["Basophilia",    "Basos > 100/µL",
         "CML (hallmark), myeloproliferative disorders, hypothyroidism, allergic states"],
    ]
    rows = [[Paragraph(h, CELL_HDR) for h in headers]]
    for i, row in enumerate(data):
        cells = [Paragraph(row[0], CELL_B),
                 Paragraph(row[1], CELL_S),
                 Paragraph(row[2], CELL_S)]
        rows.append(cells)

    col_w = [33*mm, 42*mm, 105*mm]
    tbl = Table(rows, colWidths=col_w, repeatRows=1)
    tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), MED_BLUE),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, GREY_BG]),
        ("GRID",          (0,0), (-1,-1), 0.4, GREY_LINE),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("LINEBELOW",     (0,0), (-1,0), 1, DEEP_BLUE),
    ]))
    return tbl


# ============================================================
# 5. PLATELET TABLE
# ============================================================
def platelet_table():
    headers = ["Finding", "Count", "Decreased Production", "Increased Destruction / Sequestration"]
    data = [
        ["Normal",              "150-400 x10\u00b3/µL",  "—",   "—"],
        ["Mild Thrombocytopenia","100-149 x10\u00b3/µL", "Observe; low bleeding risk", "ITP (early)"],
        ["Moderate",            "50-99 x10\u00b3/µL",
         "B12/folate def., chemotherapy, alcohol",
         "ITP, DIC, HIT, HUS/TTP, HELLP"],
        ["Severe",              "< 50 x10\u00b3/µL",
         "Aplastic anemia, leukemia, infiltration",
         "Sepsis, massive transfusion, cardiopulmonary bypass"],
        ["Thrombocytosis",      "> 400 x10\u00b3/µL",
         "Reactive: infection, iron def., post-splenectomy",
         "Primary: Essential thrombocythemia, PV, CML"],
    ]
    rows = [[Paragraph(h, CELL_HDR) for h in headers]]
    row_bgs = [HexColor("#E8F5E9"), HexColor("#FFF9C4"), HexColor("#FFE0B2"),
               HexColor("#FFCDD2"), HexColor("#E3F2FD")]
    for i, row in enumerate(data):
        cells = [Paragraph(row[0], CELL_B)] + [Paragraph(c, CELL_S) for c in row[1:]]
        rows.append(cells)

    col_w = [33*mm, 30*mm, 54*mm, 63*mm]
    tbl = Table(rows, colWidths=col_w, repeatRows=1)
    tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), MED_BLUE),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [HexColor("#E8F5E9"), HexColor("#FFF9C4"),
                                           HexColor("#FFE0B2"), HexColor("#FFCDD2"),
                                           HexColor("#E3F2FD")]),
        ("GRID",          (0,0), (-1,-1), 0.4, GREY_LINE),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("LINEBELOW",     (0,0), (-1,0), 1, DEEP_BLUE),
    ]))
    return tbl


# ============================================================
# 6. CLINICAL PEARLS (2-column layout)
# ============================================================
def pearls_block():
    pearls = [
        ("ANC Formula",
         "ANC = Total WBC x (% Segs + % Bands)\nSevere neutropenia: ANC < 500 — high infection risk"),
        ("Anemia in Acute Hemorrhage",
         "Hb/Hct may be NORMAL initially (takes 12-24 h for plasma to equilibrate). Check reticulocytes + clinical context."),
        ("Left Shift",
         "Increased bands/metamyelocytes in peripheral blood. Suggests active bacterial infection or sepsis. May correct the automated WBC count upward."),
        ("Pancytopenia",
         "All 3 cell lines low. Consider: aplastic anemia, B12/folate def., hypersplenism, bone marrow infiltration, leukemia."),
        ("Reactive vs. Leukemic Leukocytosis",
         "Leukemoid reaction (>50,000/µL) — check LAP score, BCR-ABL, and smear morphology. Blasts suggest leukemia."),
        ("Smear Indications",
         "Order peripheral smear for: suspected hemolysis, RBC inclusions, blast cells, unexplained cytopenias, thrombocytopenia, myelodysplasia."),
        ("RDW Significance",
         "Elevated RDW (>14.5%) = anisocytosis. Helps differentiate iron def. anemia (high RDW) from thalassemia trait (normal RDW, low MCV)."),
        ("Polycythemia Vera Clues",
         "Elevated RBC + WBC + Platelets together. Check JAK2 V617F mutation. Erythropoietin level low (vs. secondary erythrocytosis)."),
    ]

    rows = []
    for i in range(0, len(pearls), 2):
        left = pearls[i]
        right = pearls[i+1] if i+1 < len(pearls) else ("", "")
        left_cell = [
            Paragraph(left[0], BOLD_S),
            Paragraph(left[1].replace("\n", "<br/>"), SMALL_S)
        ]
        right_cell = [
            Paragraph(right[0], BOLD_S),
            Paragraph(right[1].replace("\n", "<br/>"), SMALL_S)
        ] if right[0] else [Paragraph("", SMALL_S)]
        rows.append([left_cell, right_cell])

    # Flatten for Table
    flat_rows = []
    for row in rows:
        flat_rows.append([row[0], row[1]])

    # We can't put lists in Table cells — convert to nested tables
    col_w_half = CONTENT_W / 2 - 2*mm

    outer_rows = []
    for row in rows:
        left_tbl = Table([[c] for c in row[0]], colWidths=[col_w_half - 6*mm])
        left_tbl.setStyle(TableStyle([
            ("TOPPADDING",    (0,0), (-1,-1), 1),
            ("BOTTOMPADDING", (0,0), (-1,-1), 1),
            ("LEFTPADDING",   (0,0), (-1,-1), 0),
            ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ]))
        right_tbl = Table([[c] for c in row[1]], colWidths=[col_w_half - 6*mm])
        right_tbl.setStyle(TableStyle([
            ("TOPPADDING",    (0,0), (-1,-1), 1),
            ("BOTTOMPADDING", (0,0), (-1,-1), 1),
            ("LEFTPADDING",   (0,0), (-1,-1), 0),
            ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ]))
        outer_rows.append([left_tbl, right_tbl])

    tbl = Table(outer_rows, colWidths=[col_w_half, col_w_half])
    tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), GREY_BG),
        ("ROWBACKGROUNDS",(0,0), (-1,-1), [GREY_BG, WHITE]),
        ("GRID",          (0,0), (-1,-1), 0.4, GREY_LINE),
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 7),
        ("RIGHTPADDING",  (0,0), (-1,-1), 7),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ]))
    return tbl


# ============================================================
# 7. FOOTER
# ============================================================
def footer_block():
    tbl = Table(
        [[Paragraph(
            "For educational reference only. Always interpret CBC in clinical context. "
            "Reference ranges may vary by laboratory and patient age. "
            "Sources: Textbook of Family Medicine 9e • Henry's Clinical Diagnosis & Management by Laboratory Methods • Junqueira's Basic Histology 17e",
            NOTE_S)]],
        colWidths=[CONTENT_W]
    )
    tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), HexColor("#ECF0F1")),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
        ("LINEABOVE",     (0,0), (-1,0), 1, GREY_LINE),
    ]))
    return tbl


# ============================================================
# BUILD STORY
# ============================================================
story = []

# Title
story.append(title_block())
story.append(Spacer(1, 5*mm))

# 1. RBC Parameters
story.append(KeepTogether([
    section_banner("1.  RED BLOOD CELL (RBC) PARAMETERS"),
    Spacer(1, 1.5*mm),
    rbc_table(),
]))
story.append(Spacer(1, 4*mm))

# 2. WBC Differential
story.append(KeepTogether([
    section_banner("2.  WHITE BLOOD CELL (WBC) DIFFERENTIAL"),
    Spacer(1, 1.5*mm),
    wbc_table(),
]))
story.append(Spacer(1, 4*mm))

# 3. Anemia Classification
story.append(KeepTogether([
    section_banner("3.  ANEMIA CLASSIFICATION BY MCV", color=HexColor("#1A5276")),
    Spacer(1, 1.5*mm),
    anemia_table(),
    Spacer(1, 1.5*mm),
    Paragraph(
        "<b>Tip:</b> In early anemia, MCV may change before Hb/Hct become abnormal. "
        "Always check reticulocyte count to assess bone marrow response.",
        NOTE_S
    ),
]))
story.append(Spacer(1, 4*mm))

# 4. WBC Abnormalities
story.append(section_banner("4.  WBC ABNORMALITIES", color=HexColor("#6E2F82")))
story.append(Spacer(1, 1.5*mm))
story.append(wbc_abnorm_table())
story.append(Spacer(1, 4*mm))

# 5. Platelet Interpretation
story.append(KeepTogether([
    section_banner("5.  PLATELET INTERPRETATION", color=HexColor("#7D3C0A")),
    Spacer(1, 1.5*mm),
    platelet_table(),
    Spacer(1, 1.5*mm),
    Paragraph(
        "<b>Bleeding risk:</b> Spontaneous bleeding rare with platelets >50,000; "
        "major surgery safe >100,000; prophylactic transfusion threshold typically <10,000 (stable) or <20,000 (fever/active bleeding).",
        NOTE_S
    ),
]))
story.append(Spacer(1, 4*mm))

# 6. Clinical Pearls
story.append(KeepTogether([
    section_banner("6.  CLINICAL PEARLS & INTERPRETATION TIPS", color=HexColor("#1A5C3A")),
    Spacer(1, 1.5*mm),
    pearls_block(),
]))
story.append(Spacer(1, 5*mm))

# Footer
story.append(footer_block())

# Build
doc.build(story)
print(f"PDF saved to: {OUTPUT}")
Running Command

cd /home/daytona/workspace/cbc-guide && python generate_cbc_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/cbc-guide/CBC_Quick_Reference.pdf

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/cbc-guide/CBC_Quick_Reference.pdf') print(f'Pages: {len(r.pages)}') for i,p in enumerate(r.pages): t = p.extract_text() print(f'--- Page {i+1} ({len(t)} chars) ---') print(t[:400]) print() "

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/cbc-guide/CBC_Quick_Reference.pdf') for i,p in enumerate(r.pages): t = p.extract_text() print(f'--- Page {i+1} FULL ---') print(t) print() "

All 6 sections are fully present across 2 pages. The PDF is complete and ready.

Your CBC Quick Reference Guide is ready:

CBC Quick Reference

PDF Document · PDF


What's inside (2 pages, A4):

SectionContent
1. RBC ParametersHb, Hct, RBC count, MCV, MCH, MCHC, RDW, Reticulocytes with male/female/SI ranges
2. WBC DifferentialAll 5 cell types with % ranges, absolute counts, and clinical roles
3. Anemia by MCVMicro/Normo/Macrocytic classification, causes, and first-line tests
4. WBC AbnormalitiesLeukocytosis, leukopenia, neutrophilia/penia, lymphocytosis/penia, eosinophilia, monocytosis, basophilia
5. Platelet InterpretationSeverity grading from normal to severe thrombocytopenia + thrombocytosis, bleeding thresholds
6. Clinical PearlsANC formula, acute hemorrhage caveat, left shift, pancytopenia, reactive vs. leukemic leukocytosis, smear indications, RDW significance, PV clues
This is a shared conversation. Sign in to Orris to start your own chat.