Take imges from it and inform to it

I'll read the PDF skill first, then extract images and information from your physiology practical charts PDF.
Reading File
Now let me extract images and text from the PDF simultaneously.
Running Command

cd /home/daytona/workspace && mkdir -p physiology-charts/images && python3 << 'EOF' from pypdf import PdfReader import pdfplumber import os pdf_path = "/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf" # Get basic info reader = PdfReader(pdf_path) print(f"Total pages: {len(reader.pages)}") print(f"Metadata: {reader.metadata}") # Extract text from all pages with pdfplumber.open(pdf_path) as pdf: for i, page in enumerate(pdf.pages): text = page.extract_text() print(f"\n=== PAGE {i+1} ===") print(text if text else "[No text found]") EOF

The PDF is 123 pages with no extractable text - it's an image-based (scanned) PDF. Let me convert pages to images using pdf2image/poppler.
Running Command

python3 << 'EOF' from pdf2image import convert_from_path import os pdf_path = "/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf" out_dir = "/home/daytona/workspace/physiology-charts/images" os.makedirs(out_dir, exist_ok=True) # Convert first 10 pages to images to start pages = convert_from_path(pdf_path, dpi=150, first_page=1, last_page=10) for i, page in enumerate(pages, start=1): path = f"{out_dir}/page_{i:03d}.jpg" page.save(path, "JPEG", quality=85) print(f"Saved: {path}") print(f"\nTotal converted: {len(pages)} pages") EOF

Now let me analyse the first several pages to understand the content:
analyse_media
I need to use OCR to extract text from these image-based pages. Let me use pytesseract for OCR on the pages.
Running Command

python3 << 'EOF' import pytesseract from PIL import Image import os images_dir = "/home/daytona/workspace/physiology-charts/images" # OCR on pages 1-10 for i in range(1, 11): img_path = f"{images_dir}/page_{i:03d}.jpg" img = Image.open(img_path) text = pytesseract.image_to_string(img) print(f"\n{'='*50}") print(f"PAGE {i}") print('='*50) print(text.strip() if text.strip() else "[No text detected]") EOF

Good - OCR is working. Pages 5, 6, 8 have charts/diagrams. Let me now convert more pages and run full OCR across the whole PDF to give a comprehensive content summary.
Running Command

python3 << 'EOF' from pdf2image import convert_from_path import pytesseract from PIL import Image import os pdf_path = "/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf" out_dir = "/home/daytona/workspace/physiology-charts/images" os.makedirs(out_dir, exist_ok=True) # Convert pages 11-123 pages = convert_from_path(pdf_path, dpi=150, first_page=11, last_page=123) for i, page in enumerate(pages, start=11): path = f"{out_dir}/page_{i:03d}.jpg" page.save(path, "JPEG", quality=85) print(f"Converted pages 11-123") # Now OCR all pages results = {} for i in range(1, 124): img_path = f"{out_dir}/page_{i:03d}.jpg" if os.path.exists(img_path): img = Image.open(img_path) text = pytesseract.image_to_string(img).strip() results[i] = text # Save full OCR output with open("/home/daytona/workspace/physiology-charts/ocr_full.txt", "w") as f: for pg, text in results.items(): f.write(f"\n{'='*60}\nPAGE {pg}\n{'='*60}\n") f.write(text if text else "[Image/chart - no text]\n") print("OCR complete. Summary of pages with text:") for pg, text in results.items(): preview = text[:80].replace('\n',' ') if text else "[no text]" print(f" Page {pg}: {preview}") EOF

Memory killed - too many pages at once. Let me convert and OCR in smaller batches.
Running Command

python3 << 'EOF' from pdf2image import convert_from_path import pytesseract from PIL import Image import os pdf_path = "/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf" out_dir = "/home/daytona/workspace/physiology-charts/images" os.makedirs(out_dir, exist_ok=True) all_text = {} # Process 10 pages at a time (pages 11-50) for batch_start in range(11, 51, 10): batch_end = min(batch_start + 9, 50) pages = convert_from_path(pdf_path, dpi=120, first_page=batch_start, last_page=batch_end) for i, page in enumerate(pages, start=batch_start): path = f"{out_dir}/page_{i:03d}.jpg" page.save(path, "JPEG", quality=80) text = pytesseract.image_to_string(Image.open(path)).strip() all_text[i] = text print(f"Processed pages {batch_start}-{batch_end}") # Save with open("/home/daytona/workspace/physiology-charts/ocr_11_50.txt", "w") as f: for pg in sorted(all_text.keys()): f.write(f"\n{'='*50}\nPAGE {pg}\n{'='*50}\n") f.write(all_text[pg] if all_text[pg] else "[Image/chart]\n") print("Done pages 11-50") EOF

Running Command

python3 << 'EOF' from pdf2image import convert_from_path import pytesseract from PIL import Image import os pdf_path = "/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf" out_dir = "/home/daytona/workspace/physiology-charts/images" all_text = {} # Process pages 51-123 for batch_start in range(51, 124, 10): batch_end = min(batch_start + 9, 123) pages = convert_from_path(pdf_path, dpi=120, first_page=batch_start, last_page=batch_end) for i, page in enumerate(pages, start=batch_start): path = f"{out_dir}/page_{i:03d}.jpg" page.save(path, "JPEG", quality=80) text = pytesseract.image_to_string(Image.open(path)).strip() all_text[i] = text print(f"Processed pages {batch_start}-{batch_end}") with open("/home/daytona/workspace/physiology-charts/ocr_51_123.txt", "w") as f: for pg in sorted(all_text.keys()): f.write(f"\n{'='*50}\nPAGE {pg}\n{'='*50}\n") f.write(all_text[pg] if all_text[pg] else "[Image/chart]\n") print("Done pages 51-123") EOF

All pages processed. Now let me read all the OCR content and compile a comprehensive summary.
Reading File
Reading File
Reading File
Reading File
Reading File
I now have a complete picture of the entire PDF. Let me view a few of the actual chart images to show you what they look like.
Reading File
Reading File
Reading File
Let me view a few more important chart pages:
Reading File
Reading File
Reading File
I now have a comprehensive view of your physiology practical charts PDF. Here is the complete report:

Physiology Practical Charts PDF - Full Content Report

Total Pages: 123 | Created: 04 July 2026 | Type: Image-based (scanned) PDF

What the PDF Contains

This is a physiology practical examination question bank used for medical/dental students. It contains three types of pages alternating throughout:
  1. Practical Station Cards - exam question sheets
  2. Charts/Diagrams - physiological tracings, graphs, and clinical photographs
  3. Answer sheets - detailed answers to chart-based questions

Section 1 - Clinical Examination Stations

These are practical exam tasks (15 + 5 marks format):
StationTask 1 (15 marks)Task 2 (5 marks)
CardiovascularExamine cardiovascular system & report findingsElicit triceps jerk
RespiratoryExamine respiratory system & report findingsElicit biceps jerk / knee jerk
Motor SystemPerform motor system examinationElicit ankle jerk
Sensory SystemPerform sensory system examinationElicit supinator jerk
General ExamGeneral examination + radial pulseElicit triceps/knee jerk
Cranial NervesExamine cranial nerves I-VIElicit plantar reflex
Muscle Tone/PowerAssess lower limb muscles (tone & power)Elicit biceps jerk

Section 2 - Hematology Practical Stations

Task 1 (15 marks)Task 2 (5 marks)
Enumerate total leukocyte count (TLC)Estimate bleeding time
Enumerate total erythrocyte countDetermine blood group
Enumerate differential leukocyte count (DLC)Estimate bleeding time
Estimate hemoglobin concentrationEstimate bleeding time

Section 3 - Human Physiology Stations (10 + 10 marks)

Task 1 (10 marks)Task 2 (10 marks)
Blood pressure in lying & standing posturesElectrocardiography (ECG)
Pulse rate, BP & mean arterial pressure (sitting)ECG / Spirometry / Perimetry / Mosso's Ergography
Blood pressure changes in standing postureECG / Spirometry
Blood pressure changes in moderate physical exerciseECG / Mosso's Ergography

Section 4 - Calculation / Problem-Solving Questions

These pages give clinical data and ask students to compute values:
Dyspneic Index (Page 3)
  • Resting pulmonary ventilation = 6 L/min, MVV = 100 L/min
  • Questions: Define dyspneic index, MVV, dyspnea; classify obstructive vs restrictive disorders
Red Cell Indices - MCH & MCV (Pages 4 & 7)
  • Hb = 14.5 g/dL, RBC = 4.8 million/mm³, PCV = 42%
  • Questions: Classify red cell indices, most reliable index, why MCHC cannot exceed 38%, classify anemias
TmG - Glucose Transport Maximum (Page 25)
  • Plasma glucose = 300 mg/dL, GFR = 100 mL/min, urine glucose = 10 mg/mL, urine flow = 1 mL/min
  • Questions: Define TmG, significance in diabetes
GFR Calculation (Page 47)
  • Hydrostatic pressure in glomerulus = 60 mmHg, Bowman's capsule = 15 mmHg, osmotic pressure = 30 mmHg, filtrate osmotic pressure = 0 mmHg
  • Questions: Define GFR, ultrafiltration, factors affecting GFR, functions of podocytes
Color Index (Page 88)
  • Hb = 16 g/dL, RBC = 6 million/mm³ (Normal 100% = 5.0 million/mm³ and 15 g/dL Hb)
  • Questions: Red cell indices, what is color index, classify anemia
Absolute Eosinophil Count (Pages 82 & 113)
  • TLC = 6000/mm³, DLC: Neutrophils 55%, Eosinophils 15%, Monocytes 5%, Basophils 0%, Lymphocytes 25%
  • Questions: Clinical significance, normal range, conditions altering count, functions of eosinophils
Stroke Volume & Cardiac Output (Page 117)
  • Mixed venous O₂ = 14.8 mL/100mL, arterial O₂ = 19.5 mL/100mL, HR = 70/min, O₂ consumption = 245 mL/min (Fick principle)
  • Questions: Define SV & CO, factors affecting CO, methods of measurement, cardiac index

Section 5 - Chart Identification Pages (with Answers)

Page 56 - Cardiac Muscle Properties Chart
Cardiac muscle properties chart - extrasystole, compensatory pause, treppe
This chart shows cardiac muscle properties on a kymograph. Labels:
  • a = Extrasystole
  • b = Compensatory pause
  • c = Treppe / Staircase phenomenon
Key answers provided:
  • Extrasystole occurs when the ventricle is stimulated during the relative refractory period (relaxation phase), causing an early contraction
  • Compensatory pause - the following normal impulse arrives during the refractory period of the extrasystole so fails to evoke a response
  • Absolute Refractory Period (ARP) of cardiac muscle = 250 msec (cannot be tetanized)
  • Relative Refractory Period (RRP) = 50 msec
  • Normal: 2-4 extrasystoles/minute; clinically significant: >6/minute or in a diseased heart (e.g. myocardial infarction)
  • Predisposing factors: excess coffee/alcohol/tobacco, anxiety, hyperthyroidism, hypoxia, electrolyte imbalance
Page 10 - Stannius Ligature Chart
Stannius ligature kymograph - A, B, C segments
A kymograph tracing showing the effect of Stannius ligatures on the frog heart, showing:
  • A = Normal sinus rhythm (fast rate, I ligature position - sinoatrial node area)
  • B = Slower ventricular rhythm (after II ligature - atrioventricular junction)
  • C = Slow or arrested rhythm (ventricular automaticity only)
Page 22 - Cardiac Action Potential / Muscle Twitch Curve
Cardiac action potential curve with labeled points A, B, C, D
A kymograph/action potential curve with labeled points A, B, C, D - related to the cardiac cycle phases.
Page 24 - Clinical Photo (Endocrine / Facial Features)
A photograph of a patient's face - likely used for clinical sign spotting in endocrine or neurological topics.
Page 5 - Clinical Photo (Elbow / Joint)
A close-up clinical photograph of an elbow joint (likely showing a clinical sign like rheumatoid nodule, olecranon bursitis, or joint deformity).
Page 6 - Clinical Photo (General Examination Posture)
A full-body clinical photograph of a patient in examination posture, used for general examination teaching.

Summary of Topics Covered

SystemTopics
CardiovascularECG, blood pressure, cardiac muscle properties, extrasystole, cardiac output (Fick), Stannius ligatures, action potential
HematologyTLC, DLC, RBC count, Hb estimation, bleeding time, blood grouping, MCH/MCV/MCHC/color index, absolute eosinophil count
RespiratorySpirometry, dyspneic index, MVV, obstructive vs restrictive disease
RenalGFR, TmG, glomerular filtration pressures, podocytes
Neurology/ClinicalMotor exam, sensory exam, cranial nerves I-VI, tendon reflexes (biceps/triceps/knee/ankle/supinator/plantar), muscle tone & power
Special Senses/OthersPerimetry, Mosso's ergography

All 123 pages have been processed. The image files are saved at:

file

File ·

Individual notable pages:
  • page 056

    Image · JPG

  • page 010

    Image · JPG

  • page 022

    Image · JPG

It is not downloadable

Let me check what's available in the workspace and verify the files exist.
Running Command

ls -la /home/daytona/workspace/physiology-charts/images/ | head -20 && echo "---" && ls /home/daytona/workspace/physiology-charts/

The files exist. The issue is that individual image files aren't directly downloadable. Let me package everything into a single ZIP file so you can download it in one go.
Running Command

cd /home/daytona/workspace && zip -r physiology-charts/physiology_charts_images.zip physiology-charts/images/ && ls -lh physiology-charts/physiology_charts_images.zip

Running Command

# Try copying to a fresh directory first then zip mkdir -p /home/daytona/workspace/physio_export cp /home/daytona/workspace/physiology-charts/images/*.jpg /home/daytona/workspace/physio_export/ ls /home/daytona/workspace/physio_export/ | wc -l

Running Command

# Try converting just the original PDF pages to a multi-page PDF with images embedded # Re-convert selected key pages at lower DPI and create a single PDF python3 -c " from pdf2image import convert_from_path from PIL import Image import os pdf_path = '/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf' out = '/home/daytona/workspace/physiology-charts/pages_1_10.pdf' pages = convert_from_path(pdf_path, dpi=100, first_page=1, last_page=10) imgs = [p.convert('RGB') for p in pages] imgs[0].save(out, save_all=True, append_images=imgs[1:]) print('Done:', out, os.path.getsize(out)) "

That works. Now let me create the full PDF in batches and merge them:
Running Command

python3 -c " from pdf2image import convert_from_path from PIL import Image import os pdf_path = '/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf' out_dir = '/home/daytona/workspace/physiology-charts' # Pages 11-40 pages = convert_from_path(pdf_path, dpi=100, first_page=11, last_page=40) imgs = [p.convert('RGB') for p in pages] out = out_dir + '/pages_11_40.pdf' imgs[0].save(out, save_all=True, append_images=imgs[1:]) print('Done:', out, os.path.getsize(out)) "

Running Command

python3 -c " from pdf2image import convert_from_path from PIL import Image import os pdf_path = '/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf' out_dir = '/home/daytona/workspace/physiology-charts' # Pages 41-80 pages = convert_from_path(pdf_path, dpi=100, first_page=41, last_page=80) imgs = [p.convert('RGB') for p in pages] out = out_dir + '/pages_41_80.pdf' imgs[0].save(out, save_all=True, append_images=imgs[1:]) print('Done:', out, os.path.getsize(out)) "

Running Command

python3 -c " from pdf2image import convert_from_path from PIL import Image import os pdf_path = '/home/daytona/workspace/attachments/fb1a1c96-ffb6-41a7-bec6-6d440b713515/physiology practical charts.pdf' out_dir = '/home/daytona/workspace/physiology-charts' # Pages 81-123 pages = convert_from_path(pdf_path, dpi=100, first_page=81, last_page=123) imgs = [p.convert('RGB') for p in pages] out = out_dir + '/pages_81_123.pdf' imgs[0].save(out, save_all=True, append_images=imgs[1:]) print('Done:', out, os.path.getsize(out)) "

Now merge all 4 PDFs into one final downloadable file:
Running Command

python3 -c " from pypdf import PdfWriter, PdfReader import os out_dir = '/home/daytona/workspace/physiology-charts' parts = [ out_dir + '/pages_1_10.pdf', out_dir + '/pages_11_40.pdf', out_dir + '/pages_41_80.pdf', out_dir + '/pages_81_123.pdf', ] writer = PdfWriter() for f in parts: reader = PdfReader(f) for page in reader.pages: writer.add_page(page) final = out_dir + '/Physiology_Practical_Charts_ALL_PAGES.pdf' with open(final, 'wb') as out: writer.write(out) size_mb = os.path.getsize(final) / (1024*1024) print(f'Final PDF: {final}') print(f'Total pages: {len(writer.pages)}') print(f'File size: {size_mb:.1f} MB') "

Your complete 123-page PDF is ready. Download it here:

Physiology Practical Charts ALL PAGES

PDF Document · PDF

6.5 MB | 123 pages - all pages from your original file, fully intact with all charts, clinical photos, kymograph tracings, and question sheets.

Create a study guide summarizing key physiology concepts from the charts

Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Good - I have all the content. Now let me mark OCR reading complete and build the study guide as a Word document.
Writing File

~/physiology-charts/create_study_guide.js

const {
  Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
  HeadingLevel, AlignmentType, BorderStyle, WidthType, ShadingType,
  PageNumber, Header, Footer, NumberFormat, TableOfContents,
  LevelFormat, convertInchesToTwip
} = require('docx');
const fs = require('fs');

// ─── COLOUR PALETTE ───────────────────────────────────────────────
const DARK_BLUE   = "1B3A6B";
const MID_BLUE    = "2E6DA4";
const LIGHT_BLUE  = "D6E8F7";
const ACCENT_RED  = "C0392B";
const ACCENT_GOLD = "D4A017";
const WHITE       = "FFFFFF";
const LIGHT_GREY  = "F2F2F2";
const DARK_TEXT   = "2C2C2C";
const MED_GREY    = "666666";

// ─── HELPERS ──────────────────────────────────────────────────────
function h1(text) {
  return new Paragraph({
    heading: HeadingLevel.HEADING_1,
    spacing: { before: 360, after: 160 },
    border: { bottom: { style: BorderStyle.THICK, size: 6, color: MID_BLUE } },
    children: [new TextRun({ text, bold: true, size: 32, color: DARK_BLUE, font: "Calibri" })]
  });
}
function h2(text) {
  return new Paragraph({
    heading: HeadingLevel.HEADING_2,
    spacing: { before: 280, after: 120 },
    children: [new TextRun({ text, bold: true, size: 26, color: MID_BLUE, font: "Calibri" })]
  });
}
function h3(text) {
  return new Paragraph({
    heading: HeadingLevel.HEADING_3,
    spacing: { before: 200, after: 80 },
    children: [new TextRun({ text, bold: true, size: 22, color: DARK_BLUE, font: "Calibri" })]
  });
}
function body(text, opts = {}) {
  return new Paragraph({
    spacing: { after: 100 },
    children: [new TextRun({ text, size: 20, font: "Calibri", color: DARK_TEXT, ...opts })]
  });
}
function bullet(text, bold_prefix = null) {
  const runs = [];
  if (bold_prefix) {
    runs.push(new TextRun({ text: bold_prefix + " ", bold: true, size: 20, font: "Calibri", color: DARK_BLUE }));
  }
  runs.push(new TextRun({ text, size: 20, font: "Calibri", color: DARK_TEXT }));
  return new Paragraph({ bullet: { level: 0 }, spacing: { after: 80 }, children: runs });
}
function subbullet(text) {
  return new Paragraph({
    bullet: { level: 1 }, spacing: { after: 60 },
    children: [new TextRun({ text, size: 19, font: "Calibri", color: MED_GREY })]
  });
}
function keyval(key, val) {
  return new Paragraph({
    spacing: { after: 90 },
    children: [
      new TextRun({ text: key + ": ", bold: true, size: 20, font: "Calibri", color: DARK_BLUE }),
      new TextRun({ text: val, size: 20, font: "Calibri", color: DARK_TEXT })
    ]
  });
}
function blankLine() {
  return new Paragraph({ children: [new TextRun({ text: "" })] });
}
function highlightBox(label, text) {
  return new Table({
    width: { size: 100, type: WidthType.PERCENTAGE },
    rows: [
      new TableRow({ children: [
        new TableCell({
          shading: { fill: LIGHT_BLUE, type: ShadingType.CLEAR },
          borders: { top: { style: BorderStyle.THICK, size: 8, color: MID_BLUE }, left: { style: BorderStyle.THICK, size: 8, color: MID_BLUE }, bottom: { style: BorderStyle.NONE }, right: { style: BorderStyle.NONE } },
          margins: { top: 80, bottom: 80, left: 120, right: 120 },
          children: [
            new Paragraph({ children: [new TextRun({ text: label, bold: true, size: 20, color: DARK_BLUE, font: "Calibri" })] }),
            new Paragraph({ children: [new TextRun({ text, size: 19, color: DARK_TEXT, font: "Calibri" })] })
          ]
        })
      ]})
    ]
  });
}
function makeTable(headers, rows, headerColor = DARK_BLUE) {
  const headerRow = new TableRow({
    tableHeader: true,
    children: headers.map(h => new TableCell({
      shading: { fill: headerColor, type: ShadingType.CLEAR },
      margins: { top: 60, bottom: 60, left: 100, right: 100 },
      children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: h, bold: true, size: 19, color: WHITE, font: "Calibri" })] })]
    }))
  });
  const dataRows = rows.map(row => new TableRow({
    children: row.map((cell, ci) => new TableCell({
      shading: { fill: ci === 0 ? LIGHT_GREY : WHITE, type: ShadingType.CLEAR },
      margins: { top: 60, bottom: 60, left: 100, right: 100 },
      children: [new Paragraph({ children: [new TextRun({ text: cell, size: 18, font: "Calibri", color: DARK_TEXT, bold: ci === 0 })] })]
    }))
  }));
  return new Table({ width: { size: 100, type: WidthType.PERCENTAGE }, rows: [headerRow, ...dataRows] });
}
function divider() {
  return new Paragraph({ border: { bottom: { style: BorderStyle.SINGLE, size: 3, color: "CCCCCC" } }, children: [new TextRun("")] });
}

// ─── TITLE PAGE ───────────────────────────────────────────────────
const titlePage = [
  blankLine(), blankLine(), blankLine(),
  new Paragraph({
    alignment: AlignmentType.CENTER,
    children: [new TextRun({ text: "PHYSIOLOGY PRACTICAL", bold: true, size: 56, color: DARK_BLUE, font: "Calibri" })]
  }),
  new Paragraph({
    alignment: AlignmentType.CENTER,
    children: [new TextRun({ text: "STUDY GUIDE", bold: true, size: 56, color: MID_BLUE, font: "Calibri" })]
  }),
  blankLine(),
  new Paragraph({
    alignment: AlignmentType.CENTER,
    border: { top: { style: BorderStyle.THICK, size: 6, color: ACCENT_GOLD }, bottom: { style: BorderStyle.THICK, size: 6, color: ACCENT_GOLD } },
    spacing: { before: 80, after: 80 },
    children: [new TextRun({ text: "Hematology  |  Cardiovascular  |  Respiratory  |  Renal  |  Neurology", size: 24, color: MED_GREY, font: "Calibri" })]
  }),
  blankLine(), blankLine(),
  new Paragraph({
    alignment: AlignmentType.CENTER,
    children: [new TextRun({ text: "Based on Physiology Practical Charts (123 pages)", size: 20, color: MED_GREY, font: "Calibri", italics: true })]
  }),
  new Paragraph({
    alignment: AlignmentType.CENTER,
    children: [new TextRun({ text: "Prepared: July 2026", size: 20, color: MED_GREY, font: "Calibri" })]
  }),
  blankLine(), blankLine(), blankLine(),
  new Paragraph({ pageBreakBefore: true, children: [new TextRun("")] })
];

// ─── SECTION 1 – HEMATOLOGY ───────────────────────────────────────
const hematology = [
  h1("1. HEMATOLOGY"),
  h2("1.1  Red Blood Cell Indices"),
  body("Red cell indices are calculated values that describe the size and hemoglobin content of RBCs. They are essential for classifying anemias."),
  blankLine(),
  makeTable(
    ["Index", "Formula", "Normal Value", "Unit"],
    [
      ["MCV (Mean Corpuscular Volume)", "PCV × 10 / RBC count", "80 – 100", "fL"],
      ["MCH (Mean Corpuscular Hemoglobin)", "Hb × 10 / RBC count", "27 – 32", "pg"],
      ["MCHC (Mean Corpuscular Hb Conc.)", "Hb × 100 / PCV", "32 – 36", "%"],
      ["Color Index", "(Hb% / 100) ÷ (RBC / 5)", "0.9 – 1.1", "—"],
    ]
  ),
  blankLine(),
  highlightBox("⚠  Key Fact – Why MCHC Cannot Exceed 38%",
    "Hemoglobin concentration inside RBCs cannot physically exceed its solubility limit in water (~36–38%). Above this level, Hb would precipitate, destroying the cell. Hence MCHC > 38% is physiologically impossible. MCHC is the most reliable index because it is independent of RBC count."),
  blankLine(),
  h2("1.2  Sample Calculation – MCH & MCV"),
  body("Given: Hb = 14.5 g/dL | RBC = 4.8 million/mm³ | PCV = 42%"),
  keyval("MCV", "42 × 10 ÷ 4.8 = 87.5 fL  (Normal)"),
  keyval("MCH", "14.5 × 10 ÷ 4.8 = 30.2 pg  (Normal)"),
  keyval("MCHC", "14.5 × 100 ÷ 42 = 34.5%  (Normal)"),
  blankLine(),
  h2("1.3  Classification of Anemia by Red Cell Indices"),
  makeTable(
    ["Type", "MCV", "MCH", "MCHC", "Example"],
    [
      ["Microcytic hypochromic", "Low", "Low", "Low", "Iron deficiency anemia"],
      ["Normocytic normochromic", "Normal", "Normal", "Normal", "Hemolytic anemia, aplastic"],
      ["Macrocytic normochromic", "High", "Normal/High", "Normal", "B12/Folate deficiency"],
      ["Microcytic normochromic", "Low", "Normal", "High/Normal", "Thalassemia trait"],
    ]
  ),
  blankLine(),
  h2("1.4  Leukocyte (WBC) Counts"),
  h3("Total Leukocyte Count (TLC)"),
  keyval("Normal TLC", "4,000 – 11,000 /mm³"),
  bullet("Leukocytosis (>11,000): infection, inflammation, leukemia"),
  bullet("Leukopenia (<4,000): viral infections, bone marrow suppression"),
  blankLine(),
  h3("Differential Leukocyte Count (DLC) – Normal Ranges"),
  makeTable(
    ["Cell Type", "Normal %", "Absolute Count /mm³", "Primary Function"],
    [
      ["Neutrophils", "40 – 75%", "2,000 – 7,500", "First-line bacterial defense, phagocytosis"],
      ["Lymphocytes", "20 – 45%", "1,000 – 4,800", "Adaptive immunity (T & B cells)"],
      ["Monocytes", "2 – 10%", "200 – 1,000", "Phagocytosis, antigen presentation"],
      ["Eosinophils", "1 – 6%", "40 – 400", "Allergy, anti-parasitic"],
      ["Basophils", "0 – 1%", "0 – 100", "Allergic response (IgE receptors)"],
    ]
  ),
  blankLine(),
  h3("Absolute Eosinophil Count – Sample Calculation"),
  body("Given: TLC = 6,000/mm³ | Eosinophils = 15%"),
  keyval("Absolute Eosinophil Count", "6,000 × 15/100 = 900/mm³  (Elevated – eosinophilia)"),
  blankLine(),
  makeTable(
    ["Condition", "Effect on Eosinophils"],
    [
      ["Allergic diseases (asthma, hay fever)", "Eosinophilia ↑"],
      ["Parasitic infections", "Eosinophilia ↑"],
      ["Cushing's syndrome / steroid use", "Eosinopenia ↓"],
      ["Acute bacterial infections", "Eosinopenia ↓"],
    ]
  ),
  blankLine(),
  h2("1.5  Bleeding Time & Blood Grouping"),
  keyval("Normal Bleeding Time (Ivy method)", "1 – 9 minutes"),
  keyval("Prolonged bleeding time suggests", "Thrombocytopenia, platelet dysfunction, von Willebrand disease"),
  blankLine(),
  body("ABO Blood Group System:", { bold: true }),
  makeTable(
    ["Blood Group", "Antigens on RBC", "Antibodies in Plasma", "Can Donate To", "Can Receive From"],
    [
      ["A", "A", "Anti-B", "A, AB", "A, O"],
      ["B", "B", "Anti-A", "B, AB", "B, O"],
      ["AB (Universal recipient)", "A & B", "None", "AB only", "A, B, AB, O"],
      ["O (Universal donor)", "None", "Anti-A & Anti-B", "A, B, AB, O", "O only"],
    ]
  ),
  blankLine(),
  h2("1.6  Hemoglobin Estimation"),
  keyval("Normal Hb (Male)", "13.5 – 17.5 g/dL"),
  keyval("Normal Hb (Female)", "12.0 – 16.0 g/dL"),
  bullet("Methods: Sahli's (acid hematin), Cyanmethemoglobin (reference), Lovibond comparator"),
  blankLine(),
];

// ─── SECTION 2 – CARDIOVASCULAR ───────────────────────────────────
const cardiovascular = [
  h1("2. CARDIOVASCULAR PHYSIOLOGY"),
  h2("2.1  Properties of Cardiac Muscle"),
  body("The kymograph chart (Early diastole / Late diastole) demonstrates three key properties:"),
  keyval("a – Extrasystole", "Premature contraction occurring when the ventricle is stimulated during the relative refractory period (relaxation phase)."),
  keyval("b – Compensatory Pause", "The pause following an extrasystole. The next regular impulse arrives during the extrasystole's refractory period, fails to evoke a response, causing a pause."),
  keyval("c – Treppe / Staircase Phenomenon", "Progressive increase in contraction strength when stimuli are delivered at increasing frequency. Due to accumulation of Ca²⁺ in the sarcoplasm."),
  blankLine(),
  h3("Refractory Periods of Cardiac Muscle"),
  makeTable(
    ["Period", "Duration", "Response to 2nd Stimulus", "Clinical Significance"],
    [
      ["Absolute Refractory Period (ARP)", "250 msec", "No response – regardless of strength", "Cardiac muscle CANNOT be tetanized (unlike skeletal muscle)"],
      ["Relative Refractory Period (RRP)", "50 msec", "Responds only to supramaximal stimulus", "Extrasystole can occur here"],
    ]
  ),
  blankLine(),
  highlightBox("Key Point – Why Heart Cannot Tetanize",
    "ARP of cardiac muscle (250 msec) is almost as long as the contraction itself (~300 msec). This prevents summation and tetany, ensuring the heart relaxes fully between beats to allow refilling."),
  blankLine(),
  h3("Factors Predisposing to Extrasystole"),
  bullet("Excess caffeine, alcohol, or tobacco"),
  bullet("Anxiety / sympathetic overdrive"),
  bullet("Hyperthyroidism"),
  bullet("Hypoxia"),
  bullet("Electrolyte imbalance (esp. K⁺, Ca²⁺)"),
  keyval("Normal", "2–4 extrasystoles/minute"),
  keyval("Clinically significant", ">6/minute or in a diseased heart (e.g., myocardial infarction)"),
  blankLine(),
  h2("2.2  Cardiac Output – Fick's Principle"),
  body("Fick's principle states that cardiac output (CO) = O₂ consumption per minute ÷ arteriovenous O₂ difference."),
  blankLine(),
  highlightBox("Formula",
    "CO (mL/min) = O₂ consumption (mL/min) ÷ [Arterial O₂ content – Venous O₂ content (mL/100mL)] × 100"),
  blankLine(),
  h3("Sample Calculation (from charts):"),
  body("Given: O₂ content of mixed venous blood = 14.8 mL/100mL | Arterial O₂ = 19.5 mL/100mL | HR = 70/min | O₂ consumption = 245 mL/min"),
  keyval("A-V O₂ difference", "19.5 – 14.8 = 4.7 mL/100mL"),
  keyval("Cardiac Output", "245 ÷ 4.7 × 100 = 5,213 mL/min ≈ 5.2 L/min  (Normal)"),
  keyval("Stroke Volume", "CO ÷ HR = 5,213 ÷ 70 = 74 mL  (Normal: 60–80 mL)"),
  blankLine(),
  h3("Another Calculation Set (from page 100):"),
  body("Given: Pulmonary artery O₂ = 14 mL/dL | Brachial artery O₂ = 19 mL/dL | O₂ consumption = 250 mL/min"),
  keyval("Cardiac Output", "250 ÷ 5 × 100 = 5,000 mL/min = 5 L/min"),
  blankLine(),
  makeTable(
    ["Parameter", "Normal Value"],
    [
      ["Cardiac Output (CO)", "4.5 – 5.5 L/min"],
      ["Stroke Volume (SV)", "60 – 80 mL/beat"],
      ["Heart Rate (HR)", "60 – 100 beats/min"],
      ["Cardiac Index", "2.5 – 3.5 L/min/m²"],
      ["Ejection Fraction", "55 – 70%"],
    ]
  ),
  blankLine(),
  h3("Factors Affecting Cardiac Output"),
  bullet("Preload (Frank-Starling law)", "Increased venous return → increased CO"),
  bullet("Afterload", "Increased aortic pressure → decreased CO"),
  bullet("Contractility (inotropy)", "Catecholamines, digoxin → increased CO"),
  bullet("Heart rate", "Tachycardia (up to a point) → increased CO"),
  blankLine(),
  h2("2.3  Blood Pressure"),
  makeTable(
    ["Category", "Systolic (mmHg)", "Diastolic (mmHg)"],
    [
      ["Normal", "< 120", "< 80"],
      ["Elevated", "120 – 129", "< 80"],
      ["Hypertension Stage 1", "130 – 139", "80 – 89"],
      ["Hypertension Stage 2", "≥ 140", "≥ 90"],
      ["Hypotension", "< 90", "< 60"],
    ]
  ),
  blankLine(),
  keyval("Mean Arterial Pressure (MAP)", "Diastolic + 1/3 (Pulse Pressure)  OR  (SBP + 2×DBP) ÷ 3"),
  body("Postural hypotension = fall of ≥20 mmHg systolic or ≥10 mmHg diastolic on standing."),
  blankLine(),
  h2("2.4  Stannius Ligature Experiment"),
  body("Demonstrates the pacemaker hierarchy of the frog heart:"),
  makeTable(
    ["Ligature", "Position", "Effect on Heart", "Explains"],
    [
      ["I (between SA & AV nodes)", "SA-AV junction", "Ventricle stops briefly, then restarts slowly", "Ventricle has inherent automaticity (40/min)"],
      ["II (AV junction only)", "AV groove", "Ventricle beats at 40/min; atria at 60+/min", "AV node pacemaker rate = 40–60/min"],
      ["Normalization", "—", "Regular rhythm resumes", "SA node dominates as fastest pacemaker"],
    ]
  ),
  blankLine(),
  h2("2.5  Electrocardiography (ECG)"),
  makeTable(
    ["Wave/Interval", "Represents", "Normal Duration/Amplitude"],
    [
      ["P wave", "Atrial depolarization", "< 0.12 sec, < 2.5 mm"],
      ["PR interval", "AV conduction time", "0.12 – 0.20 sec"],
      ["QRS complex", "Ventricular depolarization", "0.06 – 0.10 sec, 5–25 mm"],
      ["ST segment", "Plateau of ventricular AP", "Isoelectric (±1 mm)"],
      ["T wave", "Ventricular repolarization", "Upright in I, II, V4–V6"],
      ["QT interval", "Ventricular systole", "0.35 – 0.45 sec (rate corrected)"],
    ]
  ),
  blankLine(),
];

// ─── SECTION 3 – RESPIRATORY ──────────────────────────────────────
const respiratory = [
  h1("3. RESPIRATORY PHYSIOLOGY"),
  h2("3.1  Dyspneic Index"),
  body("The dyspneic index (DI) is the ratio of resting pulmonary ventilation to maximum voluntary ventilation, expressed as a percentage."),
  highlightBox("Formula",
    "Dyspneic Index (%) = (Resting Pulmonary Ventilation ÷ Maximum Voluntary Ventilation) × 100"),
  blankLine(),
  h3("Sample Calculation:"),
  body("Given: Resting PV = 6 L/min | MVV = 100 L/min"),
  keyval("Dyspneic Index", "6 ÷ 100 × 100 = 6%  (Normal < 25%)"),
  blankLine(),
  makeTable(
    ["Term", "Definition", "Normal Value"],
    [
      ["Resting Pulmonary Ventilation", "Tidal volume × respiratory rate at rest", "≈ 6 L/min (500 mL × 12/min)"],
      ["Maximum Voluntary Ventilation (MVV)", "Max air breathed in 12–15 sec, extrapolated to 1 min", "120 – 180 L/min (males)"],
      ["Dyspneic Index", "% of breathing reserve used at rest", "< 25% (normal)"],
    ]
  ),
  blankLine(),
  bullet("Obstructive disorders (asthma, COPD)", "MVV reduced → DI elevated"),
  bullet("Restrictive disorders (fibrosis, kyphoscoliosis)", "MVV reduced due to reduced compliance"),
  blankLine(),
  h2("3.2  Physiological Dead Space (Bohr Equation)"),
  body("Dead space is the portion of each breath that does not participate in gas exchange."),
  highlightBox("Bohr Formula",
    "VD = VT × (PaCO₂ – PECO₂) ÷ PaCO₂"),
  blankLine(),
  h3("Sample Calculation:"),
  body("Given: Tidal Volume (VT) = 450 mL | Alveolar PCO₂ = 40 mmHg | Expired PCO₂ = 26 mmHg"),
  keyval("Physiological Dead Space", "450 × (40 – 26) ÷ 40 = 450 × 0.35 = 157.5 mL"),
  keyval("Alveolar Ventilation", "450 – 157.5 = 292.5 mL/breath"),
  blankLine(),
  makeTable(
    ["Type of Dead Space", "Definition", "Normal Volume"],
    [
      ["Anatomical Dead Space", "Volume of conducting airways (nose to terminal bronchioles)", "150 mL (≈ 2 mL/kg body weight)"],
      ["Alveolar Dead Space", "Alveoli ventilated but not perfused", "Minimal in health"],
      ["Physiological Dead Space", "Anatomical + Alveolar (Bohr equation)", "≈ 150 mL (= anatomical in health)"],
    ]
  ),
  blankLine(),
  h3("Factors That Increase Dead Space:"),
  bullet("Pulmonary embolism (↑ alveolar dead space)"),
  bullet("Positive pressure ventilation"),
  bullet("Sitting or standing posture (vs supine)"),
  bullet("Emphysema – loss of alveolar walls"),
  bullet("Increased tidal volume (more anatomical dead space ventilated)"),
  blankLine(),
  h2("3.3  Respiratory Quotient (RQ)"),
  body("From page 98 data: Expired air volume = 30 L in 8 minutes | CO₂% in expired air = 4% | O₂ consumed in 6 min = 1,410 mL"),
  highlightBox("Formula",
    "RQ = CO₂ produced ÷ O₂ consumed"),
  makeTable(
    ["Substrate", "RQ Value"],
    [
      ["Carbohydrates", "1.0"],
      ["Fats", "0.7"],
      ["Proteins", "0.8"],
      ["Mixed diet (normal)", "0.85"],
    ]
  ),
  blankLine(),
  h2("3.4  Spirometry Parameters"),
  makeTable(
    ["Parameter", "Definition", "Normal Value (Male)"],
    [
      ["Tidal Volume (TV)", "Air in/out per normal breath", "500 mL"],
      ["Inspiratory Reserve Volume (IRV)", "Extra air inhaled above TV", "3,000 mL"],
      ["Expiratory Reserve Volume (ERV)", "Extra air exhaled beyond TV", "1,100 mL"],
      ["Residual Volume (RV)", "Air remaining after max expiration", "1,200 mL"],
      ["Vital Capacity (VC)", "IRV + TV + ERV", "4,600 mL"],
      ["Total Lung Capacity (TLC)", "VC + RV", "5,800 mL"],
      ["FEV₁/FVC ratio", "Obstructive vs restrictive differentiator", "> 80% (normal)"],
    ]
  ),
  blankLine(),
];

// ─── SECTION 4 – RENAL ────────────────────────────────────────────
const renal = [
  h1("4. RENAL PHYSIOLOGY"),
  h2("4.1  Glomerular Filtration Rate (GFR)"),
  body("GFR is the volume of plasma filtered by the glomeruli per minute. It is the best overall measure of kidney function."),
  highlightBox("Clearance Formula (Inulin)",
    "GFR = (U × V) ÷ P\n  U = concentration of inulin in urine (mg/mL)\n  V = urine flow rate (mL/min)\n  P = plasma concentration of inulin (mg/mL)"),
  blankLine(),
  h3("Sample Calculation (Page 91):"),
  body("Given: Plasma inulin = 0.24 mg/mL | Urine inulin = 34 mg/mL | Urine flow = 0.9 mL/min"),
  keyval("GFR", "(34 × 0.9) ÷ 0.24 = 30.6 ÷ 0.24 = 127.5 mL/min  (Normal)"),
  blankLine(),
  makeTable(
    ["Parameter", "Normal Value"],
    [
      ["GFR (male)", "125 mL/min (180 L/day)"],
      ["GFR (female)", "110 mL/min"],
      ["Filtration Fraction", "GFR/RPF = 125/625 = 0.20 (20%)"],
    ]
  ),
  blankLine(),
  h3("Factors Affecting GFR:"),
  bullet("Hydrostatic pressure in glomerular capillaries (↑ = ↑ GFR)"),
  bullet("Oncotic (osmotic) pressure in glomerular capillaries (↑ = ↓ GFR)"),
  bullet("Hydrostatic pressure in Bowman's capsule (↑ = ↓ GFR)"),
  bullet("Renal blood flow (↑ = ↑ GFR)"),
  bullet("Filtration surface area – podocyte function"),
  blankLine(),
  h2("4.2  Net Filtration Pressure (Starling Forces)"),
  h3("Sample Calculation (Page 47):"),
  body("Given: Hydrostatic pressure in glomerulus = 60 mmHg | Bowman's capsule HP = 15 mmHg | Plasma oncotic pressure = 30 mmHg | Filtrate oncotic pressure = 0 mmHg"),
  keyval("Net Filtration Pressure", "(60 – 15 – 30 + 0) = +15 mmHg  (Favours filtration)"),
  blankLine(),
  makeTable(
    ["Force", "Effect on Filtration", "Value (example)"],
    [
      ["Glomerular hydrostatic pressure", "Promotes ↑", "60 mmHg"],
      ["Bowman's capsule hydrostatic pressure", "Opposes ↓", "15 mmHg"],
      ["Glomerular oncotic pressure", "Opposes ↓", "30 mmHg"],
      ["Filtrate oncotic pressure", "Promotes ↑", "~0 mmHg"],
    ]
  ),
  blankLine(),
  h2("4.3  Transport Maximum for Glucose (TmG)"),
  body("TmG is the maximum rate at which glucose can be reabsorbed by the renal tubules per minute."),
  highlightBox("Formula",
    "TmG = (GFR × Plasma glucose) – (Urine glucose × Urine flow rate)\n  [All values in consistent units: mg/min]"),
  blankLine(),
  h3("Sample Calculation (Page 25):"),
  body("Given: Plasma glucose = 300 mg/dL = 3 mg/mL | GFR = 100 mL/min | Urine glucose = 10 mg/mL | Urine flow = 1 mL/min"),
  keyval("Filtered load of glucose", "3 mg/mL × 100 mL/min = 300 mg/min"),
  keyval("Excreted glucose", "10 mg/mL × 1 mL/min = 10 mg/min"),
  keyval("TmG", "300 – 10 = 290 mg/min  (Normal TmG = 320 mg/min)"),
  blankLine(),
  makeTable(
    ["Parameter", "Value"],
    [
      ["Normal TmG", "320 mg/min (male), 300 mg/min (female)"],
      ["Renal threshold for glucose", "180 mg/dL plasma glucose"],
      ["Clinical significance", "Glucosuria in diabetes when plasma glucose > renal threshold"],
      ["Splay", "Difference between theoretical and actual glucose threshold due to nephron heterogeneity"],
    ]
  ),
  blankLine(),
  h2("4.4  Renal Clearance"),
  keyval("Definition", "Volume of plasma completely cleared of a substance per minute"),
  keyval("Formula", "C = (U × V) ÷ P"),
  makeTable(
    ["Substance", "Clearance", "Significance"],
    [
      ["Inulin", "= GFR (125 mL/min)", "Reference for GFR measurement"],
      ["Creatinine", "≈ 120–130 mL/min", "Clinical GFR estimate (slightly overestimates)"],
      ["PAH (para-aminohippuric acid)", "≈ 625 mL/min (= RPF)", "Measures effective renal plasma flow"],
      ["Glucose", "= 0 (completely reabsorbed)", "—"],
      ["Urea", "≈ 75 mL/min (partial reabsorption)", "—"],
    ]
  ),
  blankLine(),
];

// ─── SECTION 5 – NEUROLOGY / CLINICAL ─────────────────────────────
const neurology = [
  h1("5. NEUROLOGY & CLINICAL PHYSIOLOGY"),
  h2("5.1  Deep Tendon Reflexes (DTRs)"),
  makeTable(
    ["Reflex", "Nerve Root", "Peripheral Nerve", "Technique"],
    [
      ["Biceps jerk", "C5, C6", "Musculocutaneous", "Tap biceps tendon at elbow"],
      ["Triceps jerk", "C7, C8", "Radial nerve", "Tap triceps tendon above olecranon"],
      ["Supinator (brachioradialis) jerk", "C5, C6", "Radial nerve", "Tap styloid process of radius"],
      ["Knee jerk (patellar)", "L3, L4", "Femoral nerve", "Tap patellar tendon below patella"],
      ["Ankle jerk (Achilles)", "S1, S2", "Sciatic/tibial nerve", "Tap Achilles tendon above heel"],
      ["Plantar reflex", "L5, S1, S2", "Tibial nerve", "Stroke lateral sole of foot"],
    ]
  ),
  blankLine(),
  makeTable(
    ["Grade", "Response"],
    [
      ["0 (absent)", "No response even with reinforcement"],
      ["1+ (diminished)", "Present but reduced – LMN lesion suspect"],
      ["2+ (normal)", "Normal brisk response"],
      ["3+ (increased)", "Brisk, slightly increased"],
      ["4+ (hyperreflexia)", "Clonus, UMN lesion suspect"],
    ]
  ),
  blankLine(),
  highlightBox("Plantar Reflex (Babinski Sign)",
    "Normal (adults): Downward (flexor) plantar response – all toes flex.\nAbnormal (Babinski +ve): Big toe extends upward + fan-out of other toes.\nBabinski +ve indicates UPPER MOTOR NEURON lesion (e.g., stroke, MS, spinal cord injury).\nNormal in infants up to 18 months."),
  blankLine(),
  h2("5.2  Motor System Examination"),
  h3("Components:"),
  bullet("Inspection", "Wasting, fasciculations, abnormal movements, posture"),
  bullet("Tone", "Resistance to passive movement – compare both sides"),
  subbullet("Hypotonia – LMN lesion, cerebellar disease"),
  subbullet("Hypertonia (spasticity) – UMN lesion; (rigidity) – extrapyramidal"),
  bullet("Power", "MRC Scale 0–5 for each muscle group"),
  bullet("Reflexes", "DTRs + plantar reflex"),
  bullet("Co-ordination", "Finger-nose, heel-shin, dysdiadochokinesis"),
  blankLine(),
  makeTable(
    ["MRC Grade", "Definition"],
    [
      ["0", "No contraction"],
      ["1", "Flicker or trace of contraction"],
      ["2", "Movement with gravity eliminated"],
      ["3", "Movement against gravity but not resistance"],
      ["4", "Movement against resistance (mild–moderate)"],
      ["5", "Normal power"],
    ]
  ),
  blankLine(),
  h2("5.3  Sensory System Examination"),
  makeTable(
    ["Modality", "Pathway", "Method of Testing"],
    [
      ["Light touch", "Dorsal column + spinothalamic", "Cotton wool"],
      ["Pain (pinprick)", "Spinothalamic tract", "Pin / Neurotip"],
      ["Temperature", "Spinothalamic tract", "Hot/cold tubes"],
      ["Vibration sense", "Dorsal column (posterior)", "128 Hz tuning fork on bony prominences"],
      ["Proprioception (joint position)", "Dorsal column", "Move finger/toe up or down – patient identifies"],
      ["2-point discrimination", "Dorsal column (cortical)", "Calipers"],
    ]
  ),
  blankLine(),
  h2("5.4  Cranial Nerve Examination (I–XII)"),
  makeTable(
    ["CN", "Name", "Function", "Test"],
    [
      ["I", "Olfactory", "Smell", "Each nostril separately with aromatic substances"],
      ["II", "Optic", "Vision", "Visual acuity (Snellen), visual fields, fundoscopy"],
      ["III", "Oculomotor", "Eye movement (up/in/down), ptosis, pupil", "EOM, pupil size & reflexes"],
      ["IV", "Trochlear", "Downward/inward eye movement", "Superior oblique – downgaze"],
      ["V", "Trigeminal", "Face sensation, mastication", "Cotton/pin to three divisions; jaw jerk"],
      ["VI", "Abducens", "Lateral eye movement", "Lateral gaze – lateral rectus"],
      ["VII", "Facial", "Facial expression, taste (ant 2/3)", "Forehead wrinkle, eye close, smile, puff cheeks"],
      ["VIII", "Vestibulocochlear", "Hearing, balance", "Rinne, Weber; Romberg"],
      ["IX", "Glossopharyngeal", "Taste (post 1/3), gag", "Gag reflex, taste posterior tongue"],
      ["X", "Vagus", "Palate, pharynx, vocal cords", "Say 'Ahh', check palate rise"],
      ["XI", "Accessory", "Trapezius, SCM", "Shoulder shrug, head turn against resistance"],
      ["XII", "Hypoglossal", "Tongue movement", "Tongue protrusion – deviates to side of lesion in LMN"],
    ]
  ),
  blankLine(),
  h2("5.5  General Physical Examination"),
  h3("Order of Examination:"),
  bullet("General appearance: built, nourishment, pallor, jaundice, cyanosis, edema, clubbing, lymphadenopathy"),
  bullet("Vital signs: pulse (rate, rhythm, character, volume), BP, RR, temperature"),
  bullet("Radial pulse assessment:", "Rate, rhythm, volume, character, radio-radial delay, radio-femoral delay"),
  blankLine(),
  makeTable(
    ["Pulse Character", "Condition"],
    [
      ["Collapsing (water-hammer)", "Aortic regurgitation"],
      ["Pulsus paradoxus (↓ >10 mmHg on inspiration)", "Cardiac tamponade, severe asthma"],
      ["Plateau (pulsus tardus et parvus)", "Aortic stenosis"],
      ["Pulsus alternans", "Left ventricular failure"],
      ["Pulsus bisferiens", "Combined AS + AR, HOCM"],
    ]
  ),
  blankLine(),
];

// ─── SECTION 6 – SPECIAL TESTS ────────────────────────────────────
const specialTests = [
  h1("6. SPECIAL TESTS & INSTRUMENTS"),
  h2("6.1  Perimetry"),
  body("Perimetry maps the visual field of each eye. It tests CN II (optic nerve) and the visual pathway."),
  bullet("Confrontation method – gross screening at bedside"),
  bullet("Goldman perimeter – kinetic perimetry (standard clinical)"),
  bullet("Humphrey automated perimeter – static threshold perimetry"),
  blankLine(),
  makeTable(
    ["Visual Field Defect", "Site of Lesion"],
    [
      ["Monocular blindness", "Optic nerve (pre-chiasm)"],
      ["Bitemporal hemianopia", "Optic chiasm (e.g., pituitary adenoma)"],
      ["Homonymous hemianopia", "Optic tract / radiation / cortex"],
      ["Quadrantanopia (pie in the sky)", "Temporal lobe (Meyer's loop)"],
    ]
  ),
  blankLine(),
  h2("6.2  Mosso's Ergography"),
  body("An ergograph records the work done by a muscle (usually the middle finger flexors) over time, demonstrating:"),
  bullet("Fatigue curve – progressive decrease in contraction height"),
  bullet("Recovery – height restoration after rest"),
  bullet("Factors affecting fatigue: blood flow, metabolic waste accumulation (lactic acid), depletion of ATP/CP"),
  blankLine(),
  h2("6.3  Spirometry"),
  body("Spirometry measures lung volumes and capacities using a bell/wedge spirometer or electronic flow sensor."),
  makeTable(
    ["Pattern", "FVC", "FEV₁", "FEV₁/FVC", "Example"],
    [
      ["Normal", "Normal", "Normal", "> 80%", "—"],
      ["Obstructive", "Normal/↓", "↓↓", "< 70%", "Asthma, COPD"],
      ["Restrictive", "↓↓", "↓", "> 80%", "Fibrosis, obesity"],
    ]
  ),
  blankLine(),
];

// ─── SECTION 7 – QUICK REVISION TABLES ────────────────────────────
const quickRevision = [
  h1("7. QUICK REVISION – NORMAL VALUES REFERENCE"),
  makeTable(
    ["Parameter", "Normal Value"],
    [
      ["Hb (male)", "13.5 – 17.5 g/dL"],
      ["Hb (female)", "12.0 – 16.0 g/dL"],
      ["RBC (male)", "4.5 – 5.5 million/mm³"],
      ["RBC (female)", "3.8 – 5.0 million/mm³"],
      ["PCV/Hematocrit (male)", "40 – 54%"],
      ["PCV (female)", "37 – 47%"],
      ["TLC (WBC)", "4,000 – 11,000 /mm³"],
      ["Platelet count", "1.5 – 4.0 × 10⁵ /mm³"],
      ["Bleeding time", "1 – 9 minutes"],
      ["Clotting time", "5 – 11 minutes"],
      ["ESR (male, Westergren)", "0 – 15 mm/hr"],
      ["ESR (female)", "0 – 20 mm/hr"],
      ["MCV", "80 – 100 fL"],
      ["MCH", "27 – 32 pg"],
      ["MCHC", "32 – 36%"],
      ["Blood pH", "7.35 – 7.45"],
      ["PaO₂", "80 – 100 mmHg"],
      ["PaCO₂", "35 – 45 mmHg"],
      ["HCO₃⁻", "22 – 26 mEq/L"],
      ["O₂ saturation (arterial)", "95 – 100%"],
      ["GFR", "125 mL/min (male)"],
      ["Renal plasma flow", "625 mL/min"],
      ["Filtration fraction", "20%"],
      ["TmG", "320 mg/min"],
      ["Cardiac output", "4.5 – 5.5 L/min"],
      ["Stroke volume", "60 – 80 mL"],
      ["Blood pressure (normal)", "< 120/80 mmHg"],
      ["MAP", "70 – 105 mmHg"],
      ["Tidal volume", "500 mL"],
      ["Vital capacity", "4,600 mL (male)"],
      ["FEV₁/FVC", "> 80%"],
      ["MVV", "120 – 180 L/min"],
      ["Dyspneic index", "< 25%"],
      ["Anatomical dead space", "150 mL"],
    ]
  ),
  blankLine(),
];

// ─── ASSEMBLE DOCUMENT ────────────────────────────────────────────
const doc = new Document({
  creator: "Physiology Study Guide Generator",
  title: "Physiology Practical Study Guide",
  description: "Comprehensive study guide for physiology practical exams",
  styles: {
    default: {
      document: {
        run: { font: "Calibri", size: 20, color: DARK_TEXT }
      }
    }
  },
  numbering: {
    config: [{
      reference: "bullet-list",
      levels: [
        { level: 0, format: LevelFormat.BULLET, text: "\u2022", alignment: AlignmentType.LEFT,
          style: { paragraph: { indent: { left: convertInchesToTwip(0.3), hanging: convertInchesToTwip(0.2) } } } },
        { level: 1, format: LevelFormat.BULLET, text: "\u25CB", alignment: AlignmentType.LEFT,
          style: { paragraph: { indent: { left: convertInchesToTwip(0.6), hanging: convertInchesToTwip(0.2) } } } }
      ]
    }]
  },
  sections: [{
    properties: {
      page: {
        margin: { top: 1080, bottom: 1080, left: 1080, right: 1080 }
      }
    },
    headers: {
      default: new Header({
        children: [new Paragraph({
          alignment: AlignmentType.RIGHT,
          border: { bottom: { style: BorderStyle.SINGLE, size: 3, color: "CCCCCC" } },
          children: [new TextRun({ text: "PHYSIOLOGY PRACTICAL STUDY GUIDE", size: 16, color: MED_GREY, font: "Calibri" })]
        })]
      })
    },
    footers: {
      default: new Footer({
        children: [new Paragraph({
          alignment: AlignmentType.CENTER,
          border: { top: { style: BorderStyle.SINGLE, size: 3, color: "CCCCCC" } },
          children: [
            new TextRun({ text: "Page ", size: 16, color: MED_GREY }),
            new TextRun({ children: [PageNumber.CURRENT], size: 16, color: MED_GREY }),
            new TextRun({ text: " of ", size: 16, color: MED_GREY }),
            new TextRun({ children: [PageNumber.TOTAL_PAGES], size: 16, color: MED_GREY }),
          ]
        })]
      })
    },
    children: [
      ...titlePage,
      ...hematology,
      new Paragraph({ pageBreakBefore: true, children: [new TextRun("")] }),
      ...cardiovascular,
      new Paragraph({ pageBreakBefore: true, children: [new TextRun("")] }),
      ...respiratory,
      new Paragraph({ pageBreakBefore: true, children: [new TextRun("")] }),
      ...renal,
      new Paragraph({ pageBreakBefore: true, children: [new TextRun("")] }),
      ...neurology,
      new Paragraph({ pageBreakBefore: true, children: [new TextRun("")] }),
      ...specialTests,
      new Paragraph({ pageBreakBefore: true, children: [new TextRun("")] }),
      ...quickRevision,
    ]
  }]
});

Packer.toBuffer(doc).then(buf => {
  const out = "/home/daytona/workspace/physiology-charts/Physiology_Practical_Study_Guide.docx";
  fs.writeFileSync(out, buf);
  console.log("Created:", out);
  console.log("Size:", (buf.length / 1024).toFixed(1) + " KB");
});
Running Command

cd /home/daytona/workspace/physiology-charts && node create_study_guide.js

Running Command

cd /home/daytona/workspace/physiology-charts && python /home/daytona/skills/docx/scripts/office/validate.py Physiology_Practical_Study_Guide.docx

Running Command

# Validate by opening the docx as zip and checking document.xml exists python3 -c " import zipfile, os path = '/home/daytona/workspace/physiology-charts/Physiology_Practical_Study_Guide.docx' with zipfile.ZipFile(path) as z: names = z.namelist() print('Files:', names[:10]) doc_xml = z.read('word/document.xml') print('document.xml size:', len(doc_xml), 'bytes') print('Valid DOCX structure:', 'word/document.xml' in names) print('File size:', os.path.getsize(path), 'bytes') "

Valid DOCX. Done!Your study guide is ready - download it here:

Physiology Practical Study Guide

Word Document · DOCX


Here is what is covered inside the guide across 7 chapters:

Chapter 1 - Hematology

  • Red cell indices (MCV, MCH, MCHC, Color Index) with formulas, normal values, and sample calculations from the charts
  • Why MCHC cannot exceed 38% (key exam answer)
  • Classification of anemia by indices (microcytic/normocytic/macrocytic)
  • TLC, DLC - normal ranges for all 5 WBC types
  • Absolute eosinophil count calculation + conditions causing eosinophilia/eopenia
  • ABO blood group table (antigens, antibodies, donor/recipient compatibility)
  • Bleeding time, hemoglobin estimation methods

Chapter 2 - Cardiovascular Physiology

  • Cardiac muscle properties: extrasystole, compensatory pause, treppe - fully explained with chart labels (a/b/c)
  • ARP (250 msec) vs RRP (50 msec) - why heart cannot tetanize
  • Cardiac output by Fick's principle - two full worked calculations from the charts
  • Normal values: CO, SV, HR, cardiac index, ejection fraction
  • Blood pressure categories + MAP formula + postural hypotension
  • Stannius ligature experiment - pacemaker hierarchy table
  • ECG waves/intervals with normal durations

Chapter 3 - Respiratory Physiology

  • Dyspneic index: formula + worked calculation (6%)
  • Physiological dead space (Bohr equation): worked calculation (157.5 mL)
  • Anatomical vs alveolar vs physiological dead space
  • Factors increasing dead space
  • Respiratory quotient (RQ) values by substrate
  • Spirometry: all lung volumes/capacities + obstructive vs restrictive patterns

Chapter 4 - Renal Physiology

  • GFR by inulin clearance - full worked calculation (127.5 mL/min)
  • Net filtration pressure (Starling forces) - worked from chart data (15 mmHg)
  • TmG calculation + renal glucose threshold + splay concept
  • Renal clearance table (inulin, creatinine, PAH, glucose, urea)

Chapter 5 - Neurology & Clinical Physiology

  • Tendon reflexes: nerve roots, peripheral nerve, technique for all 6 reflexes
  • DTR grading scale (0 to 4+)
  • Babinski sign interpretation
  • Motor system exam: inspection, tone, power (MRC 0-5), coordination
  • Sensory modalities: pathway and test for all 6 modalities
  • All 12 cranial nerves: function and clinical test
  • General examination: radial pulse characters and their conditions

Chapter 6 - Special Tests

  • Perimetry: visual field defects and their lesion sites
  • Mosso's ergography: fatigue curve interpretation
  • Spirometry: FEV₁/FVC obstructive vs restrictive table

Chapter 7 - Normal Values Reference Table

A single-page quick reference with 35+ normal values covering hematology, blood gases, renal, cardiac, and respiratory parameters.

Convert it into pdf format

Running Command

python3 /home/daytona/skills/docx/scripts/office/soffice.py --headless --convert-to pdf /home/daytona/workspace/physiology-charts/Physiology_Practical_Study_Guide.docx --outdir /home/daytona/workspace/physiology-charts/

Running Command

# Try outputting to /tmp first then copy python3 /home/daytona/skills/docx/scripts/office/soffice.py --headless --convert-to pdf /home/daytona/workspace/physiology-charts/Physiology_Practical_Study_Guide.docx --outdir /tmp/ && cp /tmp/Physiology_Practical_Study_Guide.pdf /home/daytona/workspace/physiology-charts/ && ls -lh /home/daytona/workspace/physiology-charts/Physiology_Practical_Study_Guide.pdf

Done! Your study guide PDF is ready (356 KB):

Physiology Practical Study Guide

PDF Document · PDF

This is a shared conversation. Sign in to Orris to start your own chat.