~/pancreatic-juice-pdf/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import PageBreak
OUTPUT = "/home/daytona/workspace/pancreatic-juice-pdf/Pancreatic_Juice_Revision.pdf"
# ── Colour palette ──────────────────────────────────────────────
C_DARK_BLUE = colors.HexColor("#1a3a5c")
C_MID_BLUE = colors.HexColor("#2563a8")
C_LIGHT_BLUE = colors.HexColor("#dbeafe")
C_ACCENT = colors.HexColor("#e65c00")
C_GREEN = colors.HexColor("#166534")
C_GREEN_BG = colors.HexColor("#dcfce7")
C_YELLOW_BG = colors.HexColor("#fef9c3")
C_RED_BG = colors.HexColor("#fee2e2")
C_HEADER_BG = colors.HexColor("#1e3a5f")
C_GRAY = colors.HexColor("#6b7280")
C_LIGHT_GRAY = colors.HexColor("#f3f4f6")
C_WHITE = colors.white
# ── Styles ───────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent="Normal", **kw):
return ParagraphStyle(name, parent=styles[parent], **kw)
title_style = make_style("MainTitle",
fontSize=20, leading=26, textColor=C_WHITE,
alignment=TA_CENTER, fontName="Helvetica-Bold",
spaceAfter=4)
subtitle_style = make_style("Subtitle",
fontSize=11, leading=15, textColor=colors.HexColor("#bfdbfe"),
alignment=TA_CENTER, fontName="Helvetica")
section_style = make_style("Section",
fontSize=12, leading=16, textColor=C_WHITE,
fontName="Helvetica-Bold", spaceAfter=4, spaceBefore=6)
sub_section_style = make_style("SubSection",
fontSize=10.5, leading=14, textColor=C_DARK_BLUE,
fontName="Helvetica-Bold", spaceBefore=6, spaceAfter=2)
body_style = make_style("Body",
fontSize=9, leading=13, textColor=colors.HexColor("#1f2937"),
fontName="Helvetica", spaceAfter=3)
bullet_style = make_style("Bullet",
fontSize=9, leading=13, textColor=colors.HexColor("#1f2937"),
fontName="Helvetica", leftIndent=14, spaceAfter=2,
bulletIndent=4)
key_style = make_style("Key",
fontSize=9, leading=13, textColor=C_GREEN,
fontName="Helvetica-Bold", leftIndent=14, spaceAfter=2,
bulletIndent=4)
note_style = make_style("Note",
fontSize=8.5, leading=12, textColor=colors.HexColor("#92400e"),
fontName="Helvetica-Oblique", leftIndent=10)
table_header_style = make_style("TH",
fontSize=8.5, leading=11, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
table_cell_style = make_style("TC",
fontSize=8.5, leading=11, textColor=colors.HexColor("#1f2937"),
fontName="Helvetica", alignment=TA_LEFT)
table_cell_center_style = make_style("TCC",
fontSize=8.5, leading=11, textColor=colors.HexColor("#1f2937"),
fontName="Helvetica", alignment=TA_CENTER)
# ── Helper builders ───────────────────────────────────────────────
def section_banner(text, bg=C_HEADER_BG):
data = [[Paragraph(text, section_style)]]
t = Table(data, colWidths=[17.6*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("ROUNDEDCORNERS", [4]),
]))
return t
def bullet(text, style=bullet_style, symbol="•"):
return Paragraph(f"{symbol} {text}", style)
def key_point(text):
return Paragraph(f"★ {text}", key_style)
def sp(h=4):
return Spacer(1, h)
def make_table(headers, rows, col_widths, row_colors=None):
header_row = [Paragraph(h, table_header_style) for h in headers]
data = [header_row]
for i, row in enumerate(rows):
styled_row = []
for j, cell in enumerate(row):
s = table_cell_center_style if j > 0 and len(row) > 2 else table_cell_style
styled_row.append(Paragraph(str(cell), s))
data.append(styled_row)
t = Table(data, colWidths=col_widths, repeatRows=1)
ts = [
("BACKGROUND", (0,0), (-1,0), C_DARK_BLUE),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C_WHITE, C_LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#d1d5db")),
("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"),
]
if row_colors:
for row_idx, bg in row_colors:
ts.append(("BACKGROUND", (0, row_idx), (-1, row_idx), bg))
t.setStyle(TableStyle(ts))
return t
def highlight_box(text, bg=C_YELLOW_BG, text_color=colors.HexColor("#78350f")):
s = make_style("HB", fontSize=9, leading=13, textColor=text_color,
fontName="Helvetica-Bold", leftIndent=6)
data = [[Paragraph(text, s)]]
t = Table(data, colWidths=[17.6*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("BOX", (0,0), (-1,-1), 0.8, colors.HexColor("#d97706")),
]))
return t
# ── Document ──────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=1.8*cm, rightMargin=1.8*cm,
topMargin=1.5*cm, bottomMargin=1.5*cm,
title="Pancreatic Juice - Quick Revision",
author="Orris Medical"
)
story = []
# ══════════════════════════════════════════════════════
# TITLE BLOCK
# ══════════════════════════════════════════════════════
title_data = [[
Paragraph("PANCREATIC JUICE", title_style),
],[
Paragraph("Secretion · Composition · Properties · Functions · Regulation", subtitle_style),
],[
Paragraph("Quick Revision Notes | Sembulingam Style | University Exam", subtitle_style),
]]
title_table = Table([[
Paragraph("PANCREATIC JUICE", title_style)],
[Paragraph("Secretion · Composition · Properties · Functions · Regulation", subtitle_style)],
[Paragraph("Quick Revision Notes | Sembulingam Style | University Exam", subtitle_style)],
], colWidths=[17.6*cm])
title_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_HEADER_BG),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 16),
("RIGHTPADDING", (0,0), (-1,-1), 16),
("ROUNDEDCORNERS",[6]),
]))
story.append(title_table)
story.append(sp(10))
# ══════════════════════════════════════════════════════
# 1. PROPERTIES
# ══════════════════════════════════════════════════════
story.append(section_banner("1. PROPERTIES OF PANCREATIC JUICE"))
story.append(sp(4))
props = [
["Property", "Value"],
["Volume / day", "1500 mL (1.5 – 2 L)"],
["Colour", "Colourless, watery, clear"],
["Reaction", "Alkaline"],
["pH", "7.6 – 8.2 (up to 8.1 at max flow)"],
["Specific gravity", "~1.010"],
["Osmolality", "Isotonic (~300 mOsm/kg) = plasma"],
]
story.append(make_table(props[0], props[1:], [6*cm, 11.6*cm]))
story.append(sp(8))
# ══════════════════════════════════════════════════════
# 2. COMPOSITION
# ══════════════════════════════════════════════════════
story.append(section_banner("2. COMPOSITION OF PANCREATIC JUICE"))
story.append(sp(4))
# 2A Electrolytes
story.append(Paragraph("A. Electrolytes (from Ductal / Centroacinar Cells)", sub_section_style))
elec = [
["Ion", "Concentration", "Changes with Flow Rate?"],
["Na⁺", "~160 mEq/L", "Constant"],
["K⁺", "~5 mEq/L", "Constant"],
["HCO₃⁻","50 → 113-145 mEq/L", "↑ RISES with flow ★"],
["Cl⁻", "Falls reciprocally", "↓ FALLS with flow"],
]
story.append(make_table(elec[0], elec[1:], [4*cm, 7*cm, 6.6*cm],
row_colors=[(3, colors.HexColor("#dcfce7")), (4, colors.HexColor("#fee2e2"))]))
story.append(sp(4))
story.append(highlight_box(
"★ EXAM KEY: As secretory rate ↑ → HCO₃⁻ ↑ and Cl⁻ ↓ (reciprocal exchange via Cl⁻/HCO₃⁻ exchanger + CFTR). Na⁺ and K⁺ remain CONSTANT at all flow rates."))
story.append(sp(8))
# 2B Enzymes - Proteases
story.append(Paragraph("B. Enzymes — PROTEASES (secreted as INACTIVE zymogens)", sub_section_style))
prot = [
["Zymogen", "Active Enzyme", "Activated By", "Action"],
["Trypsinogen", "Trypsin", "Enterokinase ★", "Cleaves Arg/Lys peptide bonds"],
["Chymotrypsinogen", "Chymotrypsin", "Trypsin", "Cleaves aromatic AA bonds"],
["Proelastase", "Elastase", "Trypsin", "Cleaves aliphatic AA bonds"],
["Procarboxypeptidase A","Carboxypeptidase A","Trypsin", "C-terminal aromatic/branched AA"],
["Procarboxypeptidase B","Carboxypeptidase B","Trypsin", "C-terminal basic AA"],
]
story.append(make_table(prot[0], prot[1:], [4*cm, 4.2*cm, 3.5*cm, 5.9*cm]))
story.append(sp(4))
story.append(highlight_box(
"★ Trypsin is the MASTER ACTIVATOR — activated by Enterokinase (brush border), it then activates ALL other zymogens (autocatalytic). Pancreatic TRYPSIN INHIBITOR co-secreted for protection against autodigestion.",
bg=C_LIGHT_BLUE, text_color=C_DARK_BLUE))
story.append(sp(8))
# 2C Lipases
story.append(Paragraph("C. Enzymes — LIPASES (secreted in ACTIVE form)", sub_section_style))
lip = [
["Enzyme", "Substrate", "Products / Notes"],
["Pancreatic Lipase", "Triglycerides", "Monoglycerides + 2 fatty acids (needs Colipase + bile salts)"],
["Phospholipase A₂", "Phospholipids", "Lyso-PC + fatty acid (secreted as prophospholipase; activated by trypsin)"],
["Cholesteryl Ester Hydrolase","Cholesteryl esters","Cholesterol + fatty acid"],
]
story.append(make_table(lip[0], lip[1:], [4.8*cm, 4*cm, 8.8*cm]))
story.append(sp(8))
# 2D Amylase + Nucleases
story.append(Paragraph("D. Other Enzymes", sub_section_style))
oth = [
["Enzyme", "Secreted As", "Activator", "Substrate & Products"],
["Pancreatic α-Amylase", "Active", "Cl⁻ ions", "Starch → Maltose, Maltotriose, α-limit dextrins"],
["RNAase", "Active", "—", "RNA → nucleotides"],
["DNAase", "Active", "—", "DNA → nucleotides"],
["Colipase", "Procolipase (inactive)", "Trypsin", "Cofactor enabling Lipase to bind fat droplets"],
]
story.append(make_table(oth[0], oth[1:], [4.2*cm, 3.2*cm, 2.8*cm, 7.4*cm]))
story.append(sp(10))
# ══════════════════════════════════════════════════════
# 3. FUNCTIONS
# ══════════════════════════════════════════════════════
story.append(section_banner("3. FUNCTIONS OF PANCREATIC JUICE"))
story.append(sp(6))
funcs = [
("<b>Protein digestion</b>: Trypsin, chymotrypsin, elastase, carboxypeptidases hydrolyse proteins → peptides + amino acids", bullet_style),
("<b>Carbohydrate digestion</b>: Amylase converts starch → maltose, maltotriose, α-limit dextrins", bullet_style),
("<b>Fat digestion</b>: Lipase, phospholipase A₂, cholesteryl ester hydrolase digest triglycerides, phospholipids, cholesterol esters", bullet_style),
("<b>Nucleic acid digestion</b>: RNAase and DNAase digest RNA and DNA", bullet_style),
("<b>Neutralisation of gastric acid</b>: HCO₃⁻ + HCl → NaCl + H₂O + CO₂ (protects duodenal mucosa; prevents duodenal ulcers)", bullet_style),
("<b>Optimal pH for enzyme activity</b>: Pancreatic enzymes work best at pH 7.0 – 8.0; alkaline juice provides this environment", bullet_style),
("<b>Facilitates fat absorption</b>: Correct pH enables bile salt micelle formation for efficient fat absorption", bullet_style),
]
for text, sty in funcs:
story.append(bullet(text, sty))
story.append(sp(2))
story.append(sp(4))
story.append(highlight_box(
"Neutralisation Reaction: HCl + NaHCO₃ → NaCl + H₂CO₃ → NaCl + H₂O + CO₂",
bg=C_GREEN_BG, text_color=C_GREEN))
story.append(sp(10))
# ══════════════════════════════════════════════════════
# 4. REGULATION — HORMONAL
# ══════════════════════════════════════════════════════
story.append(section_banner("4. REGULATION OF PANCREATIC SECRETION"))
story.append(sp(6))
story.append(Paragraph("A. Primary Hormonal Regulators", sub_section_style))
horm = [
["Hormone", "Source", "Stimulus", "Target Cell", "Effect", "2nd Messenger"],
["SECRETIN", "S cells\n(duodenum/jejunum)", "H⁺ acid in duodenum\n(pH < 4.5)", "Ductal cells", "↑ Large vol.\nHCO₃⁻-rich,\nenzyme-POOR", "cAMP ↑"],
["CCK\n(Pancreozymin)", "I cells\n(duodenum/jejunum)", "Fats, proteins,\namino acids", "Acinar cells", "↑ Enzyme-rich,\nsmall volume", "IP₃/Ca²⁺ ↑"],
["ACh\n(Vagus)", "Parasympathetic\nnerve endings", "Cephalic stimuli;\ngastric distension", "Acinar cells", "↑ Enzyme\nsecretion;\npotentiates CCK", "IP₃/Ca²⁺ ↑"],
]
story.append(make_table(horm[0], horm[1:], [2.8*cm, 3.2*cm, 3.2*cm, 2.6*cm, 3.2*cm, 2.6*cm]))
story.append(sp(4))
story.append(highlight_box(
"★★ EXAM KEY: Secretin → DUCTS → HCO₃⁻ (cAMP) | CCK → ACINI → Enzymes (IP₃/Ca²⁺) | They POTENTIATE each other: combined effect > sum of individual effects"))
story.append(sp(8))
# 4B Phases
story.append(Paragraph("B. Three Phases of Pancreatic Secretion", sub_section_style))
phases = [
["Phase", "Stimulus", "Mediator", "% of Total Output"],
["1. Cephalic", "Sight, smell, taste,\nchewing, conditioned reflex", "Vagus nerve → ACh", "20–25%"],
["2. Gastric", "Gastric distension\nafter food ingestion", "Vagovagal reflex → ACh", "10%"],
["3. Intestinal ★\n(DOMINANT)", "H⁺, fats, proteins entering duodenum", "Secretin + CCK +\nenteropancreatic reflexes", "65–70%"],
]
story.append(make_table(phases[0], phases[1:], [3.5*cm, 5.5*cm, 4.8*cm, 3.8*cm],
row_colors=[(3, colors.HexColor("#fef9c3"))]))
story.append(sp(8))
# 4C Mechanism HCO3
story.append(Paragraph("C. Mechanism of HCO₃⁻ Secretion by Ductal Cells", sub_section_style))
steps = [
"CO₂ diffuses from blood into ductal cells",
"CO₂ + H₂O → H₂CO₃ → <b>H⁺ + HCO₃⁻</b> (carbonic anhydrase)",
"<b>HCO₃⁻</b> secreted into duct lumen via apical <b>Cl⁻/HCO₃⁻ exchanger</b> (requires CFTR to supply Cl⁻)",
"H⁺ extruded basolaterally via Na⁺/H⁺ exchanger",
"Na⁺ follows HCO₃⁻ into lumen (electroosmosis via tight junctions)",
"H₂O follows osmotically → isotonic alkaline juice formed",
]
for i, s in enumerate(steps, 1):
story.append(bullet(f"<b>Step {i}:</b> {s}", bullet_style, symbol=f"{i}."))
story.append(sp(2))
story.append(sp(4))
story.append(highlight_box(
"CF Link: Defective CFTR → impaired HCO₃⁻ secretion → thick viscous secretions → protein plugs → chronic pancreatitis / exocrine insufficiency",
bg=C_RED_BG, text_color=colors.HexColor("#991b1b")))
story.append(sp(8))
# 4D Inhibitors
story.append(Paragraph("D. Inhibitors of Pancreatic Secretion", sub_section_style))
inhib = [
["Inhibitor", "Effect"],
["Somatostatin", "Inhibits BOTH enzyme and HCO₃⁻ secretion"],
["Glucagon", "Inhibits enzyme secretion"],
["Pancreatic Polypeptide (PP)","Inhibits exocrine secretion"],
["Sympathetic (α-adrenergic)","Suppresses interdigestive secretion"],
]
story.append(make_table(inhib[0], inhib[1:], [6*cm, 11.6*cm]))
story.append(sp(10))
# ══════════════════════════════════════════════════════
# 5. QUICK REVISION TABLE
# ══════════════════════════════════════════════════════
story.append(section_banner("5. QUICK REVISION — 10 MUST-KNOW EXAM FACTS", bg=colors.HexColor("#7c3aed")))
story.append(sp(6))
facts = [
["#", "Key Fact"],
["1", "Volume = 1500 mL/day | pH = 7.6–8.2 (alkaline)"],
["2", "HCO₃⁻ is HIGHEST in any body fluid when stimulated (~113–145 mEq/L)"],
["3", "Enterokinase activates Trypsinogen → Trypsin activates ALL other zymogens"],
["4", "Secretin → Ducts → HCO₃⁻ (cAMP) | CCK → Acini → Enzymes (IP₃/Ca²⁺)"],
["5", "CCK + Secretin + ACh act together and POTENTIATE each other"],
["6", "Intestinal phase is DOMINANT (65–70% of total output)"],
["7", "As flow rate ↑ → HCO₃⁻ ↑ and Cl⁻ ↓ (reciprocal); Na⁺/K⁺ stay constant"],
["8", "Neutralisation: HCl + NaHCO₃ → NaCl + H₂O + CO₂"],
["9", "Trypsin inhibitor is co-secreted — protects against autodigestion"],
["10", "Acute pancreatitis → serum AMYLASE and LIPASE rise (diagnostic markers)"],
]
story.append(make_table(facts[0], facts[1:], [1.2*cm, 16.4*cm],
row_colors=[(i, C_YELLOW_BG) for i in [2, 4, 6, 8, 10]]))
story.append(sp(10))
# ══════════════════════════════════════════════════════
# Footer note
# ══════════════════════════════════════════════════════
story.append(HRFlowable(width="100%", thickness=0.5, color=C_GRAY))
story.append(sp(4))
footer_s = make_style("Footer",
fontSize=7.5, textColor=C_GRAY, fontName="Helvetica-Oblique",
alignment=TA_CENTER)
story.append(Paragraph(
"Prepared by Orris Medical AI • Based on Sembulingam / Ganong / Guyton & Hall / Costanzo Physiology • For exam revision only",
footer_s))
# ── Build ─────────────────────────────────────────────────────────
doc.build(story)
print("PDF created:", OUTPUT)