Investigations in malabsotption syndtom

| Test | Details |
|---|---|
| Quantitative fecal fat | Patient ingests 70-100 g fat/day for 2 days; stool collected for 72 hours. Normal: <7 g fat/24 hr. Values >30 g/day strongly suggest pancreatic insufficiency |
| Qualitative stool fat (Sudan III stain) | Spot test - specific for fecal fat; high sensitivity (90%) and specificity (90%). Useful screening test; not quantitative |
| NMR method | Determines % fat in stool; normal <20%; requires adequate fat intake (100 g/day) |
A negative fecal fat test points toward carbohydrate malabsorption or secretory/osmotic diarrhea rather than fat malabsorption. A positive test directs further workup.
| Investigation | What it detects |
|---|---|
| CBC | Macrocytic anemia (B12/folate deficiency), microcytic anemia (iron deficiency), lymphopenia |
| Serum iron, TIBC, ferritin | Iron deficiency - suggests proximal small bowel (duodenal) disease |
| Serum B12 and folate | B12 deficiency - ileal disease or bacterial overgrowth; folate - proximal mucosal disease |
| Serum albumin | Hypoalbuminemia - protein malabsorption or protein-losing enteropathy |
| Prothrombin time / INR | Prolonged if vitamin K (fat-soluble) malabsorption present |
| Serum calcium, phosphate, ALP | Hypocalcemia + raised ALP - vitamin D deficiency, osteomalacia |
| Serum vitamins A, D, E | Fat-soluble vitamin deficiencies |
| Serum magnesium, zinc | Micronutrient deficiencies |
| ESR / CRP | General inflammatory markers; raised in IBD |
| Serum cholesterol | Low cholesterol in severe fat malabsorption; absent in abetalipoproteinemia |
| Breath Test | Principle | What it diagnoses |
|---|---|---|
| Hydrogen (H2) breath test - lactulose/glucose | Bacteria ferment unabsorbed sugars → H2 exhaled. Early H2 rise (<90 min) suggests SIBO | Small intestinal bacterial overgrowth (SIBO) |
| H2 breath test - lactose | Rise in H2 after lactose ingestion | Lactase deficiency |
| H2 breath test - fructose/sucrose | Rise in H2 | Carbohydrate malabsorption |
| ¹⁴C-glycerol trioleate breath test | Labeled triglyceride ingested; ¹⁴CO₂ in expired air measured; decreased CO₂ = fat malabsorption | Steatorrhea (pancreatic vs. mucosal - with 2-stage test + enzyme supplementation) |
| ¹⁴C or ¹³C D-xylose breath test | Alternative to urine xylose test | Small bowel mucosal disease |
| Bile acid ¹⁴C-cholylglycine breath test | Deconjugated by bacteria → ¹⁴CO₂ exhaled | Bile acid malabsorption, SIBO |
| Investigation | Indication |
|---|---|
| CT abdomen | Pancreatic calcifications, structural lesions, lymphadenopathy, bowel wall thickening |
| MR enterography | Mucosal disease of small bowel, Crohn's disease; no radiation |
| MRCP | Pancreatic duct anatomy, biliary disease |
| Small bowel barium study | Transit time, diverticula, strictures |
| Somatostatin receptor scintigraphy / PET dotatate | Neuroendocrine tumors (carcinoid, VIPoma, gastrinoma) causing secretory diarrhea |
| Test | Purpose |
|---|---|
| Sweat chloride test | Cystic fibrosis (CF) - main cause of pancreatic insufficiency in children |
| Serum gastrin | Zollinger-Ellison syndrome (excess acid inactivates lipase) |
| Serum VIP, chromogranin A, 5-HIAA (urine) | Neuroendocrine tumors |
| Serum calcitonin, glucagon, somatostatin | Medullary thyroid carcinoma, glucagonoma, somatostatinoma |
| Serum beta-lipoprotein | Absent in abetalipoproteinemia |
| Serum immunoglobulins | Agammaglobulinemia (absent humoral immunity → susceptibility to Giardia) |
| Intestinal permeability tests (lactulose-mannitol ratio) | Mucosal integrity assessment; used in celiac disease research |
| Fecal calprotectin | Differentiates IBD from IBS; monitors disease activity |
| Fecal alpha-1 antitrypsin | Protein-losing enteropathy |
| Category | Key Test | Main Purpose |
|---|---|---|
| Stool | Fecal fat (quantitative/qualitative), Sudan stain | Confirm steatorrhea |
| Stool | Fecal elastase-1 | Pancreatic insufficiency |
| Stool | O&P, Giardia EIA/PCR | Infectious cause |
| Blood | CBC, iron, B12, folate, albumin, PT | Nutritional deficiencies |
| Blood | Anti-TTG IgA + total IgA | Celiac disease |
| Absorption | D-xylose test | Mucosal vs. pancreatic |
| Absorption | Schilling test | B12/ileal function |
| Breath | H2 breath test | SIBO, lactose/carbohydrate malabsorption |
| Breath | ¹⁴C-trioleate breath test | Fat absorption |
| Pancreas | Secretin test / secretin-MRCP / fecal elastase | Exocrine pancreatic function |
| Imaging | CT/MR enterography | Structural disease |
| Endoscopy | Duodenal biopsy | Mucosal disease (celiac, Whipple's) |
Make pdf with white baxkground
ls /home/daytona/workspace/ 2>/dev/null || mkdir -p /home/daytona/workspace
mkdir -p /home/daytona/workspace/malabsorption-pdf
~/malabsorption-pdf/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import Image as RLImage
import urllib.request
import os
OUTPUT = "/home/daytona/workspace/malabsorption-pdf/Malabsorption_Syndrome_Investigations.pdf"
# Download the diagnostic flowchart image
img_url = "https://cdn.orris.care/cdss_images/49924f361a9e45c2cd75ed8abb9e8da72f9f5297e9cf215ff12848b38d9f09de.png"
img_path = "/home/daytona/workspace/malabsorption-pdf/flowchart.png"
if not os.path.exists(img_path):
urllib.request.urlretrieve(img_url, img_path)
print("Image downloaded.")
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2*cm, bottomMargin=2*cm,
title="Investigations in Malabsorption Syndrome",
author="Orris Medical"
)
W, H = A4
content_width = W - 4*cm
# ── Styles ──────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()
title_style = ParagraphStyle(
"Title", parent=base["Title"],
fontSize=20, fontName="Helvetica-Bold",
textColor=colors.HexColor("#1a3a5c"),
spaceAfter=4, alignment=TA_CENTER,
backColor=colors.white
)
subtitle_style = ParagraphStyle(
"Subtitle", parent=base["Normal"],
fontSize=10, fontName="Helvetica",
textColor=colors.HexColor("#4a6fa5"),
spaceAfter=12, alignment=TA_CENTER
)
h1_style = ParagraphStyle(
"H1", parent=base["Heading1"],
fontSize=13, fontName="Helvetica-Bold",
textColor=colors.white,
backColor=colors.HexColor("#1a3a5c"),
spaceBefore=14, spaceAfter=6,
leftIndent=-0.3*cm, rightIndent=-0.3*cm,
borderPad=5
)
h2_style = ParagraphStyle(
"H2", parent=base["Heading2"],
fontSize=11, fontName="Helvetica-Bold",
textColor=colors.HexColor("#1a3a5c"),
spaceBefore=10, spaceAfter=4,
borderPad=2
)
body_style = ParagraphStyle(
"Body", parent=base["Normal"],
fontSize=9, fontName="Helvetica",
textColor=colors.HexColor("#222222"),
spaceAfter=4, leading=13,
alignment=TA_JUSTIFY
)
bullet_style = ParagraphStyle(
"Bullet", parent=base["Normal"],
fontSize=9, fontName="Helvetica",
textColor=colors.HexColor("#222222"),
spaceAfter=2, leading=13,
leftIndent=14, bulletIndent=0
)
note_style = ParagraphStyle(
"Note", parent=base["Normal"],
fontSize=8.5, fontName="Helvetica-Oblique",
textColor=colors.HexColor("#555555"),
spaceAfter=4, leading=12,
backColor=colors.HexColor("#f0f4fa"),
borderPad=6, leftIndent=4, rightIndent=4
)
source_style = ParagraphStyle(
"Source", parent=base["Normal"],
fontSize=7.5, fontName="Helvetica-Oblique",
textColor=colors.HexColor("#777777"),
spaceAfter=2, leading=11
)
# ── Helper: section header ───────────────────────────────────────────────────
def section(text):
return [
Spacer(1, 6),
Paragraph(f" {text}", h1_style),
Spacer(1, 4)
]
def subsection(text):
return [Paragraph(text, h2_style)]
def body(text):
return Paragraph(text, body_style)
def bullet(text):
return Paragraph(f"• {text}", bullet_style)
def note(text):
return Paragraph(text, note_style)
def source(text):
return Paragraph(f"<i>{text}</i>", source_style)
# ── Table style factory ──────────────────────────────────────────────────────
def make_table(data, col_widths=None, header=True):
tbl = Table(data, colWidths=col_widths, repeatRows=1 if header else 0)
style_cmds = [
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#1a3a5c")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 8.5),
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 8.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.white, colors.HexColor("#eef2f8")]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#aaaaaa")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING",(0,0), (-1,-1), 5),
("WORDWRAP", (0,0), (-1,-1), True),
]
tbl.setStyle(TableStyle(style_cmds))
return tbl
# ── Build story ──────────────────────────────────────────────────────────────
story = []
# Title block
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Investigations in Malabsorption Syndrome", title_style))
story.append(Paragraph("A Comprehensive Clinical Reference", subtitle_style))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor("#1a3a5c")))
story.append(Spacer(1, 0.3*cm))
# ── 1. Diagnostic Algorithm ──────────────────────────────────────────────────
story += section("1. Diagnostic Algorithm")
story.append(body(
"The investigation of malabsorption follows a stepwise approach. Begin with stool examination "
"to confirm fat malabsorption, then branch into disease-specific investigations based on clinical suspicion."
))
story.append(Spacer(1, 6))
# Insert flowchart
img = RLImage(img_path, width=content_width, height=content_width*0.6)
story.append(img)
story.append(Spacer(1, 4))
story.append(note(
"Figure: Approach to the diagnosis of malabsorption. Ab = antibody; CT = computed tomography; "
"EIA = enzyme immunoassay; MRCP = MR cholangiopancreatography; O&P = ova and parasites; "
"TTG = tissue transglutaminase. [Goldman-Cecil Medicine]"
))
# ── 2. Stool Examination ─────────────────────────────────────────────────────
story += section("2. Stool Examination (First-Line)")
story += subsection("2a. Stool for Ova, Parasites & Pathogens")
for b in [
"First test if chronic diarrhea is present",
"PCR or antigen-capture ELISA for <i>Giardia</i> and <i>Cryptosporidium</i>",
"Multiplex PCR stool panel for broad pathogen screening",
]:
story.append(bullet(b))
story.append(Spacer(1, 6))
story += subsection("2b. Fecal Fat Tests")
story.append(body("The cornerstone tests for confirming fat malabsorption (steatorrhea):"))
story.append(Spacer(1, 4))
fat_data = [
["Test", "Details / Normal Values"],
["Quantitative Fecal Fat\n(72-hr collection)",
"Patient ingests 70–100 g fat/day for 2 days. Stool collected 72 hrs.\n"
"Normal: <7 g fat/24 hr. Values >30 g/day strongly suggest pancreatic insufficiency.\n"
"False positives: mineral oil laxatives, rectal suppositories."],
["Qualitative Stool Fat\n(Sudan III stain)",
"Spot test — specific for fecal fat. Sensitivity 90%, Specificity 90%.\n"
"Useful screening; not quantitative. Requires adequate fat intake (100 g/day)."],
["NMR Method",
"Determines % fat in stool. Normal <20%. High sensitivity/specificity."],
]
story.append(make_table(fat_data, col_widths=[3.5*cm, content_width-3.5*cm]))
story.append(Spacer(1, 4))
story.append(note(
"A negative fecal fat test points toward carbohydrate malabsorption or secretory/osmotic diarrhea. "
"A positive test directs further workup based on clinical suspicion."
))
story.append(source("Goldman-Cecil Medicine, Table 126-6, p. 1465"))
story.append(Spacer(1, 6))
story += subsection("2c. Fecal Elastase-1")
for b in [
"Proteolytic enzyme produced by pancreas; 5–6× concentrated in feces",
"Cutoff: <200 µg/g stool = pancreatic insufficiency",
"Single 100-mg stool sample is adequate; test on formed stool only",
"Not affected by pancreatic enzyme replacement therapy",
"Sensitive for severe insufficiency; less reliable for mild-moderate disease",
"Superior to fecal chymotrypsin, PABA, bentiromide, and pancreolauryl tests",
]:
story.append(bullet(b))
story.append(source("Henry's Clinical Diagnosis and Management by Laboratory Methods"))
# ── 3. Blood Tests ────────────────────────────────────────────────────────────
story += section("3. Blood Tests")
blood_data = [
["Investigation", "What It Detects"],
["CBC (Full Blood Count)", "Macrocytic anemia (B12/folate ↓), microcytic anemia (iron ↓), lymphopenia"],
["Serum iron, TIBC, ferritin", "Iron deficiency → proximal small bowel (duodenal) disease"],
["Serum B12 and folate", "B12 ↓: ileal disease or SIBO; Folate ↓: proximal mucosal disease"],
["Serum albumin", "Hypoalbuminemia → protein malabsorption or protein-losing enteropathy"],
["Prothrombin time / INR", "Prolonged if vitamin K (fat-soluble) malabsorption present"],
["Serum calcium, phosphate, ALP", "Hypocalcaemia + raised ALP → vitamin D deficiency, osteomalacia"],
["Serum vitamins A, D, E", "Fat-soluble vitamin deficiencies"],
["Serum magnesium, zinc", "Micronutrient deficiencies"],
["ESR / CRP", "General inflammatory markers; raised in IBD"],
["Serum cholesterol", "Low in severe fat malabsorption; absent in abetalipoproteinemia"],
["Anti-TTG IgA + total IgA", "Celiac disease screening; total IgA rules out false-negative TTG"],
]
story.append(make_table(blood_data, col_widths=[4.5*cm, content_width-4.5*cm]))
# ── 4. Specific Absorption Tests ──────────────────────────────────────────────
story += section("4. Specific Absorption Tests")
story += subsection("4a. D-Xylose Absorption Test")
for b in [
"Key test to distinguish pancreatic maldigestion from mucosal (enterogenous) malabsorption",
"Patient fasts overnight then ingests <b>25 g D-xylose</b> orally",
"Urine collected for <b>5 hours</b>: normal excretion ≥4–5 g; abnormal if <3 g",
"D-xylose absorbed passively in small intestine — does NOT require pancreatic enzymes",
"Low urine excretion → <b>mucosal disease</b> (e.g., celiac disease, Whipple's)",
"Normal urine excretion → suggests <b>pancreatic insufficiency</b> (intact mucosa)",
"In renal disease: measure <b>serum D-xylose at 2 hours</b> instead",
"False positives: renal dysfunction, ascites, bacterial overgrowth (bacteria metabolize xylose)",
]:
story.append(bullet(b))
story.append(source("Henry's Clinical Diagnosis and Management, p. 281"))
story.append(Spacer(1, 6))
story += subsection("4b. Schilling Test (Vitamin B12 Absorption)")
for b in [
"Oral radioactive B12 given, followed by intramuscular non-radioactive B12 (flushing dose)",
"Radioactivity measured in 24-hour urine",
"<b>Stage 1</b> (B12 alone): Abnormal in ileal disease, SIBO, or intrinsic factor deficiency",
"<b>Stage 2</b> (B12 + intrinsic factor): Correction → pernicious anemia/gastric disease; No correction → ileal disease",
"Largely replaced by serum B12 testing but conceptually important",
]:
story.append(bullet(b))
story.append(source("Henry's Clinical Diagnosis and Management, p. 283"))
story.append(Spacer(1, 6))
story += subsection("4c. Breath Tests")
breath_data = [
["Breath Test", "Principle", "Diagnoses"],
["H₂ breath test\n(lactulose/glucose)",
"Bacteria ferment unabsorbed sugars → H₂ exhaled",
"SIBO: early H₂ rise <90 min\n(50 g glucose: sensitivity 90%)"],
["H₂ breath test\n(lactose)",
"Rise in H₂ after lactose ingestion",
"Lactase deficiency"],
["H₂ breath test\n(fructose/sucrose)",
"Rise in H₂",
"Carbohydrate malabsorption"],
["¹⁴C-glycerol trioleate\nbreath test",
"Labeled TG ingested; ↓ ¹⁴CO₂ = fat malabsorption",
"Steatorrhea; 2-stage test ± enzymes differentiates pancreatic vs. mucosal"],
["¹⁴C/¹³C D-xylose\nbreath test",
"Alternative to urine xylose",
"Small bowel mucosal disease"],
["Bile acid\n¹⁴C-cholylglycine test",
"Deconjugated by bacteria → ¹⁴CO₂",
"Bile acid malabsorption, SIBO"],
]
story.append(make_table(breath_data, col_widths=[3.5*cm, 5.5*cm, content_width-9*cm]))
story.append(source("Goldman-Cecil Medicine, Table 126-6; Yamada's Textbook of Gastroenterology 7th Ed."))
# ── 5. Disease-Specific Tests ─────────────────────────────────────────────────
story += section("5. Disease-Specific Tests")
story += subsection("5a. Pancreatic Insufficiency")
for b in [
"<b>Fecal elastase-1</b>: <200 µg/g stool (see Section 2c)",
"<b>Secretin stimulation test</b>: Gold standard — duodenal intubation; measures HCO₃⁻ and enzymes after IV secretin; sensitive but invasive",
"<b>Secretin-enhanced MRCP</b>: Spleen:pancreas signal ratio; sensitivity 77%, specificity 83% for exocrine dysfunction",
"<b>Abdominal X-ray/CT</b>: Pancreatic calcifications (30–40% of alcohol-related chronic pancreatitis)",
"<b>Sweat chloride test</b>: For cystic fibrosis (main cause of pancreatic insufficiency in children)",
]:
story.append(bullet(b))
story.append(Spacer(1, 6))
story += subsection("5b. Celiac Disease")
for b in [
"<b>Anti-tissue transglutaminase (anti-TTG) IgA</b>: Best initial serological test",
"<b>Total serum IgA</b>: Must check to exclude IgA deficiency (causes false-negative TTG)",
"<b>Anti-endomysial antibody (EMA)</b>: High specificity",
"<b>Anti-deamidated gliadin peptide (DGP) IgG</b>: Used when IgA deficient",
"<b>Duodenal biopsy (gold standard)</b>: Villous atrophy, crypt hyperplasia, intraepithelial lymphocytosis",
]:
story.append(bullet(b))
story.append(Spacer(1, 6))
story += subsection("5c. Bacterial Overgrowth (SIBO)")
for b in [
"<b>Quantitative culture of jejunal aspirate</b>: Gold standard — >10⁴ CFU/mL of coliform/aerobic organisms",
"<b>Glucose/lactulose H₂ breath test</b>: Early H₂ peak within 90 min; false positives with rapid transit",
"<b>CT/MR enterography</b>: Structural abnormalities predisposing to overgrowth (diverticula, strictures)",
]:
story.append(bullet(b))
# ── 6. Imaging ────────────────────────────────────────────────────────────────
story += section("6. Imaging")
imaging_data = [
["Investigation", "Indication"],
["CT abdomen", "Pancreatic calcifications, structural lesions, lymphadenopathy, bowel wall thickening"],
["MR enterography", "Mucosal disease of small bowel, Crohn's disease; no radiation — preferred over barium"],
["MRCP", "Pancreatic duct anatomy, biliary disease"],
["Small bowel barium study", "Transit time assessment, diverticula, strictures, mucosal fold pattern"],
["Somatostatin receptor\nscintigraphy / PET dotatate",
"Neuroendocrine tumors (carcinoid, VIPoma, gastrinoma) causing secretory diarrhea"],
]
story.append(make_table(imaging_data, col_widths=[4.5*cm, content_width-4.5*cm]))
# ── 7. Endoscopy & Biopsy ─────────────────────────────────────────────────────
story += section("7. Endoscopy and Biopsy")
for b in [
"<b>Upper GI endoscopy with multiple duodenal biopsies</b>: Proximal mucosal disease (celiac, Whipple's, giardiasis, eosinophilic enteritis)",
"<b>Colonoscopy with biopsies</b>: Distal disease, microscopic colitis",
"<b>Capsule endoscopy</b>: Mucosal lesions in middle and distal small bowel missed on standard endoscopy",
]:
story.append(bullet(b))
story.append(Spacer(1, 6))
story += subsection("Key Histological Findings")
histo_data = [
["Finding", "Diagnosis"],
["Villous atrophy + crypt hyperplasia + intraepithelial lymphocytes", "Celiac disease"],
["PAS-positive foamy macrophages + dilated lacteals (H&E)", "Whipple disease (Tropheryma whipplei)"],
["Lymphangiectasia (dilated lymphatics)", "Protein-losing enteropathy"],
["Fat globules inside enterocytes; absent beta-lipoprotein", "Abetalipoproteinemia"],
["Granulomas + skip lesions + transmural inflammation", "Crohn's disease"],
]
story.append(make_table(histo_data, col_widths=[7*cm, content_width-7*cm]))
story.append(source("Yamada's Textbook of Gastroenterology 7th Ed., p. 3401–3418"))
# ── 8. Other Specialized Tests ────────────────────────────────────────────────
story += section("8. Other Specialized Tests")
other_data = [
["Test", "Purpose"],
["Serum gastrin", "Zollinger-Ellison syndrome (excess acid inactivates lipase)"],
["Serum VIP, chromogranin A, urine 5-HIAA", "Neuroendocrine tumors (VIPoma, carcinoid)"],
["Serum calcitonin, glucagon, somatostatin", "Medullary thyroid carcinoma, glucagonoma, somatostatinoma"],
["Serum beta-lipoprotein", "Absent in abetalipoproteinemia"],
["Serum immunoglobulins", "Agammaglobulinemia → susceptibility to Giardia"],
["Lactulose-mannitol intestinal permeability test", "Mucosal integrity assessment"],
["Fecal calprotectin", "Differentiates IBD from IBS; monitors disease activity"],
["Fecal alpha-1 antitrypsin", "Protein-losing enteropathy"],
]
story.append(make_table(other_data, col_widths=[5.5*cm, content_width-5.5*cm]))
# ── 9. Summary Quick Reference ────────────────────────────────────────────────
story += section("9. Summary — Quick Reference")
summary_data = [
["Category", "Key Test", "Main Purpose"],
["Stool", "Fecal fat (quantitative/qualitative), Sudan stain", "Confirm steatorrhea"],
["Stool", "Fecal elastase-1", "Pancreatic insufficiency"],
["Stool", "O&P, Giardia EIA/PCR", "Infectious cause"],
["Blood", "CBC, iron, B12, folate, albumin, PT", "Nutritional deficiencies"],
["Blood", "Anti-TTG IgA + total IgA", "Celiac disease"],
["Absorption", "D-xylose test", "Mucosal vs. pancreatic malabsorption"],
["Absorption", "Schilling test", "B12 absorption / ileal function"],
["Breath", "H₂ breath test", "SIBO, lactose/carbohydrate malabsorption"],
["Breath", "¹⁴C-trioleate breath test", "Fat absorption"],
["Pancreas", "Secretin test / secretin-MRCP / fecal elastase", "Exocrine pancreatic function"],
["Imaging", "CT/MR enterography", "Structural small bowel disease"],
["Endoscopy", "Duodenal biopsy", "Mucosal disease (celiac, Whipple's)"],
]
story.append(make_table(
summary_data,
col_widths=[3*cm, 7*cm, content_width-10*cm]
))
# ── Sources ────────────────────────────────────────────────────────────────────
story.append(Spacer(1, 8))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor("#aaaaaa")))
story.append(Spacer(1, 4))
story.append(Paragraph("<b>Sources</b>", body_style))
for src in [
"Goldman-Cecil Medicine, International Edition, 26th Ed. (Table 126-6, p. 1465)",
"Henry's Clinical Diagnosis and Management by Laboratory Methods (pp. 271–285)",
"Sleisenger and Fordtran's Gastrointestinal and Liver Disease",
"Yamada's Textbook of Gastroenterology, 7th Ed.",
"Frameworks for Internal Medicine",
]:
story.append(source(f"• {src}"))
# ── Build ──────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUTPUT}")
Make pdf