I have sent you a PDF that is 107 pages named as Eponyms, which has all the eponyms and names, signs, symptoms, classification systems, grading systems of entire subject of surgery from Bailey and Love. What I wanted to do is turn that into, you know, a little handwritten notes kind of thing and add, you know, images wherever they are necessary. Like if the topic is about thyroid, you know, add a little thyroid image insert wherever they are necessary, like I just mentioned, and change the font to a little, you know, medico notes type handwritten format and send the same PDF without altering the information in it and send it to me again with the following changes. Thank you.
cd /home/daytona/workspace/attachments/9f0a77fd-a844-45c4-9678-be9b40b85ded && python3 -c " import pdfplumber with pdfplumber.open('Eponyms.pdf') as pdf: print(f'Total pages: {len(pdf.pages)}') for i in range(min(4, len(pdf.pages))): page = pdf.pages[i] text = page.extract_text() print(f'\n--- PAGE {i+1} ---') print(text[:1500] if text else '[No text extracted]') "
# Check available handwriting fonts in the system fc-list | grep -i -E "handwrit|caveat|indie|pacifico|dancing|kalam|patrick|architect|gochi|schoolbell|amatic|gloria|covered|nanum|permanent|reenie|walter|yanone|pangolin|satisfy|neucha|chalk" 2>/dev/null | head -40 echo "---" # Also check what fonts are in common font directories ls /usr/share/fonts/ 2>/dev/null | head -30 ls /home/daytona/.fonts/ 2>/dev/null | head -20 ls /home/daytona/workspace/ 2>/dev/null | head -20
# Check truetype fonts available ls /usr/share/fonts/truetype/ 2>/dev/null echo "---" fc-list | grep -i "ttf\|otf" | head -50 echo "---" # Check if we can download/use Google fonts python3 -c "import reportlab; print(reportlab.__version__)" python3 -c "from reportlab.pdfbase import pdfmetrics; from reportlab.pdfbase.ttfonts import TTFont; print('TTFont OK')"
# Download a handwriting-style Google font (Kalam - medical notes style) mkdir -p /home/daytona/workspace/eponyms-notes/fonts cd /home/daytona/workspace/eponyms-notes/fonts # Download Kalam (handwriting font) and Patrick Hand wget -q "https://github.com/google/fonts/raw/main/ofl/kalam/Kalam-Regular.ttf" -O Kalam-Regular.ttf wget -q "https://github.com/google/fonts/raw/main/ofl/kalam/Kalam-Bold.ttf" -O Kalam-Bold.ttf wget -q "https://github.com/google/fonts/raw/main/ofl/patrickhand/PatrickHand-Regular.ttf" -O PatrickHand-Regular.ttf ls -la
python3 -c " import pdfplumber all_text = [] with pdfplumber.open('/home/daytona/workspace/attachments/9f0a77fd-a844-45c4-9678-be9b40b85ded/Eponyms.pdf') as pdf: for i, page in enumerate(pdf.pages): text = page.extract_text() all_text.append(text or '') # Print section headings to understand structure for i, t in enumerate(all_text): lines = t.split('\n') for line in lines: if line.isupper() or (line.startswith(tuple('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')) and len(line) < 80 and not line.startswith('→') and not line.startswith('•')): stripped = line.strip() if len(stripped) > 3 and stripped not in ['', '---']: print(f'P{i+1}: {stripped}') " | head -120
~/eponyms-notes/build_notes.py
#!/usr/bin/env python3
"""
Convert Eponyms.pdf into a handwritten medical-notes style PDF
with contextual anatomical/medical images inserted at topic changes.
"""
import pdfplumber
import re
import os
import subprocess
import json
import base64
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, HRFlowable,
KeepTogether, Image as RLImage, Table, TableStyle
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from io import BytesIO
import urllib.request
# ─── Paths ──────────────────────────────────────────────────────────────────
INPUT_PDF = "/home/daytona/workspace/attachments/9f0a77fd-a844-45c4-9678-be9b40b85ded/Eponyms.pdf"
OUTPUT_PDF = "/home/daytona/workspace/eponyms-notes/Eponyms_HandwrittenNotes.pdf"
FONTS_DIR = "/home/daytona/workspace/eponyms-notes/fonts"
IMG_DIR = "/home/daytona/workspace/eponyms-notes/images"
os.makedirs(IMG_DIR, exist_ok=True)
# ─── Register Fonts ─────────────────────────────────────────────────────────
pdfmetrics.registerFont(TTFont('Kalam', f"{FONTS_DIR}/Kalam-Regular.ttf"))
pdfmetrics.registerFont(TTFont('KalamBold', f"{FONTS_DIR}/Kalam-Bold.ttf"))
pdfmetrics.registerFont(TTFont('Patrick', f"{FONTS_DIR}/PatrickHand-Regular.ttf"))
# ─── Colours (notebook aesthetic) ───────────────────────────────────────────
PAPER_BG = colors.HexColor("#FFF9F0") # warm cream
LINE_COLOR = colors.HexColor("#B0C4DE") # light steel-blue ruled line
HEADING_COLOR = colors.HexColor("#1a3a5c") # deep navy
SUBHEAD_COLOR = colors.HexColor("#2e6e9e") # medium blue
ARROW_COLOR = colors.HexColor("#c0392b") # red arrow text
BULLET_COLOR = colors.HexColor("#27ae60") # green bullets
SEPARATOR_CLR = colors.HexColor("#aaaaaa")
TITLE_COLOR = colors.HexColor("#8B0000") # dark red title
# ─── Paragraph Styles ───────────────────────────────────────────────────────
def make_styles():
S = {}
S['cover_title'] = ParagraphStyle(
'cover_title', fontName='KalamBold', fontSize=36,
textColor=TITLE_COLOR, leading=44, alignment=TA_CENTER, spaceAfter=6
)
S['cover_sub'] = ParagraphStyle(
'cover_sub', fontName='Kalam', fontSize=13,
textColor=SUBHEAD_COLOR, leading=18, alignment=TA_CENTER, spaceAfter=4
)
S['chapter'] = ParagraphStyle(
'chapter', fontName='KalamBold', fontSize=20,
textColor=HEADING_COLOR, leading=26, spaceBefore=14, spaceAfter=4,
borderPad=4
)
S['section'] = ParagraphStyle(
'section', fontName='KalamBold', fontSize=14,
textColor=SUBHEAD_COLOR, leading=20, spaceBefore=10, spaceAfter=3
)
S['eponym'] = ParagraphStyle(
'eponym', fontName='KalamBold', fontSize=12,
textColor=HEADING_COLOR, leading=17, spaceBefore=6, spaceAfter=1
)
S['arrow'] = ParagraphStyle(
'arrow', fontName='Kalam', fontSize=11,
textColor=colors.HexColor("#333333"), leading=16, spaceBefore=0, spaceAfter=2,
leftIndent=14
)
S['bullet'] = ParagraphStyle(
'bullet', fontName='Kalam', fontSize=11,
textColor=colors.HexColor("#333333"), leading=15, spaceBefore=0, spaceAfter=1,
leftIndent=22
)
S['normal'] = ParagraphStyle(
'normal', fontName='Kalam', fontSize=11,
textColor=colors.HexColor("#222222"), leading=16, spaceBefore=2, spaceAfter=2
)
S['separator'] = ParagraphStyle(
'separator', fontName='Kalam', fontSize=10,
textColor=SEPARATOR_CLR, leading=14, spaceBefore=2, spaceAfter=2,
alignment=TA_CENTER
)
S['img_caption'] = ParagraphStyle(
'img_caption', fontName='Kalam', fontSize=9,
textColor=colors.HexColor("#555555"), leading=13, spaceBefore=2, spaceAfter=6,
alignment=TA_CENTER
)
return S
# ─── Image keyword map (topic keyword → Wikimedia Commons image URL) ─────────
# We use stable Wikipedia image URLs (commons.wikimedia.org)
IMAGE_MAP = {
# Anatomy / Organs
"thyroid": ("https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Illu_thyroid_parathyroid.jpg/320px-Illu_thyroid_parathyroid.jpg", "Thyroid & Parathyroid Glands"),
"parathyroid": ("https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Illu_thyroid_parathyroid.jpg/320px-Illu_thyroid_parathyroid.jpg", "Parathyroid Glands"),
"breast": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/e5/Breast_anatomy_normal_scheme.png/280px-Breast_anatomy_normal_scheme.png", "Breast Anatomy"),
"liver": ("https://upload.wikimedia.org/wikipedia/commons/thumb/f/f5/Liver_from_front.png/280px-Liver_from_front.png", "Liver Anatomy"),
"pancreas": ("https://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Pancreas_-_Illustration_of_the_digestive_system_in_a_simple_art_style.png/320px-Pancreas_-_Illustration_of_the_digestive_system_in_a_simple_art_style.png", "Pancreas"),
"appendix": ("https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Appendix_diagram.svg/200px-Appendix_diagram.svg.png", "Appendix Location"),
"colon": ("https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Colon_anatomy.svg/320px-Colon_anatomy.svg.png", "Large Intestine Anatomy"),
"rectum": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Rectum_and_anal_canal.svg/200px-Rectum_and_anal_canal.svg.png", "Rectum & Anal Canal"),
"anal": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Rectum_and_anal_canal.svg/200px-Rectum_and_anal_canal.svg.png", "Anal Canal Anatomy"),
"inguinal": ("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Inguinal_region_and_hernia.svg/320px-Inguinal_region_and_hernia.svg.png", "Inguinal Region"),
"hernia": ("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Inguinal_region_and_hernia.svg/320px-Inguinal_region_and_hernia.svg.png", "Hernia Anatomy"),
"stomach": ("https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Stomach_diagram.svg/280px-Stomach_diagram.svg.png", "Stomach Anatomy"),
"oesophagus": ("https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Esophagus_diagram.svg/160px-Esophagus_diagram.svg.png", "Oesophagus"),
"esophagus": ("https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Esophagus_diagram.svg/160px-Esophagus_diagram.svg.png", "Oesophagus"),
"kidney": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Kidney_BL.svg/200px-Kidney_BL.svg.png", "Kidney Anatomy"),
"adrenal": ("https://upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Illu_adrenal_gland.jpg/200px-Illu_adrenal_gland.jpg", "Adrenal Gland"),
"spleen": ("https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Spleen_1.JPG/200px-Spleen_1.JPG", "Spleen"),
"gallbladder": ("https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/Liver_gallbladder.jpg/280px-Liver_gallbladder.jpg", "Gallbladder & Bile Ducts"),
"bile": ("https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/Liver_gallbladder.jpg/280px-Liver_gallbladder.jpg", "Biliary System"),
"vascular": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Aorta_branches.svg/200px-Aorta_branches.svg.png", "Aorta & Branches"),
"aorta": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Aorta_branches.svg/200px-Aorta_branches.svg.png", "Aorta"),
"carotid": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Aorta_branches.svg/200px-Aorta_branches.svg.png", "Carotid Arteries"),
"neck": ("https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Neckstructures.png/200px-Neckstructures.png", "Neck Anatomy"),
"parotid": ("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Salivary_glands.svg/240px-Salivary_glands.svg.png", "Salivary Glands"),
"salivary": ("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Salivary_glands.svg/240px-Salivary_glands.svg.png", "Salivary Glands"),
"lung": ("https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Lungs_diagram_simple.svg/200px-Lungs_diagram_simple.svg.png", "Lung Anatomy"),
"thorax": ("https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Lungs_diagram_simple.svg/200px-Lungs_diagram_simple.svg.png", "Thorax"),
"chest": ("https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Lungs_diagram_simple.svg/200px-Lungs_diagram_simple.svg.png", "Chest Anatomy"),
"hand": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/76/Bones_of_the_left_hand_-_anterior_view.jpg/200px-Bones_of_the_left_hand_-_anterior_view.jpg", "Hand Bones"),
"skin": ("https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Skin_layers.svg/200px-Skin_layers.svg.png", "Skin Layers"),
"wound": ("https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Skin_layers.svg/200px-Skin_layers.svg.png", "Skin & Wound"),
"burn": ("https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Skin_layers.svg/200px-Skin_layers.svg.png", "Burn Depth / Skin"),
"lymph": ("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Illu_lymph_capillary.png/200px-Illu_lymph_capillary.png", "Lymphatic System"),
"nerve": ("https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Neuron.svg/280px-Neuron.svg.png", "Nerve Cell"),
"brain": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/73/Lobes_of_the_brain_NL.svg/240px-Lobes_of_the_brain_NL.svg.png", "Brain Lobes"),
"skull": ("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Human_skull_side.svg/200px-Human_skull.svg.png", "Skull"),
"bone": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Bone_structure.svg/200px-Bone_structure.svg.png", "Bone Structure"),
"fracture": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Bone_structure.svg/200px-Bone_structure.svg.png", "Bone Fracture"),
"urology": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Kidney_BL.svg/200px-Kidney_BL.svg.png", "Urological Anatomy"),
"bladder": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Kidney_BL.svg/200px-Kidney_BL.svg.png", "Urinary Bladder"),
"prostate": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Kidney_BL.svg/200px-Kidney_BL.svg.png", "Prostate"),
"testis": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Kidney_BL.svg/200px-Kidney_BL.svg.png", "Testis"),
"scrotum": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Kidney_BL.svg/200px-Kidney_BL.svg.png", "Scrotal Anatomy"),
"varicose": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Aorta_branches.svg/200px-Aorta_branches.svg.png", "Venous System"),
"vein": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Aorta_branches.svg/200px-Aorta_branches.svg.png", "Venous Anatomy"),
"artery": ("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Aorta_branches.svg/200px-Aorta_branches.svg.png", "Arterial Anatomy"),
"hip": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Bone_structure.svg/200px-Bone_structure.svg.png", "Hip Joint"),
"shoulder": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Bone_structure.svg/200px-Bone_structure.svg.png", "Shoulder Joint"),
"orthopaedic": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Bone_structure.svg/200px-Bone_structure.svg.png", "Orthopaedics"),
"orthopedic": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Bone_structure.svg/200px-Bone_structure.svg.png", "Orthopaedics"),
"ureter": ("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Kidney_BL.svg/200px-Kidney_BL.svg.png", "Ureter"),
"small intestine": ("https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Colon_anatomy.svg/320px-Colon_anatomy.svg.png", "Small Intestine"),
"duodenum": ("https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Colon_anatomy.svg/320px-Colon_anatomy.svg.png", "Duodenum"),
"peritoneum": ("https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Colon_anatomy.svg/320px-Colon_anatomy.svg.png", "Peritoneal Cavity"),
"trauma": ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Bone_structure.svg/200px-Bone_structure.svg.png", "Trauma"),
"laparoscop": ("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a3/Laparoscopy_camera.jpg/240px-Laparoscopy_camera.jpg", "Laparoscopy"),
"endoscop": ("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a3/Laparoscopy_camera.jpg/240px-Laparoscopy_camera.jpg", "Endoscopy"),
}
# Sections that strongly indicate a topic change (and what image keyword to use)
SECTION_IMAGE_KEYWORDS = {
"THYROID": "thyroid",
"PARATHYROID": "parathyroid",
"BREAST": "breast",
"LIVER": "liver",
"HEPAT": "liver",
"PANCREA": "pancreas",
"APPENDIX": "appendix",
"APPEND": "appendix",
"COLON": "colon",
"COLORECT": "colon",
"RECTUM": "rectum",
"ANAL": "anal",
"ANORECTAL": "anal",
"INGUINAL": "inguinal",
"HERNIA": "hernia",
"STOMACH": "stomach",
"GASTRIC": "stomach",
"OESOPH": "oesophagus",
"ESOPH": "esophagus",
"KIDNEY": "kidney",
"RENAL": "kidney",
"ADRENAL": "adrenal",
"SPLEEN": "spleen",
"SPLENIC": "spleen",
"GALLBLADDER": "gallbladder",
"BILIARY": "bile",
"BILE": "bile",
"VASCULAR": "vascular",
"AORTA": "aorta",
"CAROTID": "carotid",
"NECK": "neck",
"PAROTID": "parotid",
"SALIVARY": "salivary",
"LUNG": "lung",
"THORAC": "thorax",
"CHEST": "chest",
"WOUND": "wound",
"SKIN": "skin",
"BURNS": "burn",
"BURN": "burn",
"LYMPH": "lymph",
"BRAIN": "brain",
"SKULL": "skull",
"HEAD": "skull",
"BONE": "bone",
"FRACTURE": "fracture",
"ORTHOPAED": "orthopaedic",
"ORTHOPED": "orthopedic",
"UROLOGY": "urology",
"UROLOG": "urology",
"BLADDER": "bladder",
"PROSTATE": "prostate",
"TESTIS": "testis",
"TESTICULAR": "testis",
"SCROTAL": "scrotum",
"VARICOSE": "varicose",
"VEIN": "vein",
"PERITON": "peritoneum",
"SMALL INTESTINE": "small intestine",
"DUODENUM": "duodenum",
"HIP": "hip",
"SHOULDER": "shoulder",
"TRAUMA": "trauma",
"LAPAROSCOP": "laparoscop",
"ENDOSCOP": "endoscop",
"INFECTION": "skin",
"SEPSIS": "skin",
"SHOCK": "vascular",
"ONCOLOGY": "lymph",
"INCISION": "skin",
}
# ─── Image download helper ───────────────────────────────────────────────────
_img_cache = {}
def download_image(url, keyword):
if keyword in _img_cache:
return _img_cache[keyword]
safe_name = re.sub(r'[^a-z0-9]', '_', keyword.lower())
ext = ".jpg" if ".jpg" in url or ".JPG" in url else ".png"
path = os.path.join(IMG_DIR, f"{safe_name}{ext}")
if os.path.exists(path) and os.path.getsize(path) > 1000:
_img_cache[keyword] = path
return path
try:
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=10) as r:
data = r.read()
with open(path, 'wb') as f:
f.write(data)
_img_cache[keyword] = path
return path
except Exception as e:
print(f" [IMG SKIP] {keyword}: {e}")
_img_cache[keyword] = None
return None
# ─── Detect which image to show for a section heading ───────────────────────
def get_image_for_section(heading_text):
upper = heading_text.upper()
for kw, img_key in SECTION_IMAGE_KEYWORDS.items():
if kw in upper:
return img_key
return None
# ─── Extract all text from PDF ───────────────────────────────────────────────
def extract_pages():
pages = []
with pdfplumber.open(INPUT_PDF) as pdf:
for page in pdf.pages:
text = page.extract_text() or ""
pages.append(text)
return pages
# ─── Classify each line ─────────────────────────────────────────────────────
def classify_line(line):
s = line.strip()
if not s:
return ('blank', s)
# Chapter heading: digits + dot + UPPERCASE words
if re.match(r'^\d+\.\s+[A-Z]', s) and s == s.upper() or re.match(r'^\d+\.\s+[A-Z][A-Z\s/]+$', s):
return ('chapter', s)
# Section heading: letter + dot + UPPERCASE or all caps short line
if re.match(r'^[A-Z]\.\s+', s) and len(s) < 80:
return ('section', s)
# Separator line ⸻ or ---
if s in ('⸻', '---', '──────────────────────────────────────────────') or set(s).issubset({'─', '-', '—', '⸻', '–', ' '}):
return ('separator', s)
# Arrow definition line
if s.startswith('→'):
return ('arrow', s)
# Bullet line
if s.startswith('•') or s.startswith('-') or s.startswith('·'):
return ('bullet', s)
# Numbered list item
if re.match(r'^\d+\.\s', s) and len(s) < 100:
return ('bullet', s)
# Classification/score sub-items (Class I, Type I, etc.)
if re.match(r'^(Class|Type|Stage|Grade|Level)\s+(I|II|III|IV|V|0|1|2|3|4|\d)', s, re.I):
return ('eponym', s)
# Looks like a definition/classification heading (mixed case, short)
if len(s) < 70 and not s.startswith('→') and not s.startswith('•'):
return ('eponym', s)
return ('normal', s)
# ─── Page background ────────────────────────────────────────────────────────
from reportlab.platypus.flowables import Flowable
class NoteBackground(Flowable):
"""Draws lined-paper background on each page."""
def __init__(self, width, height):
Flowable.__init__(self)
self.width = width
self.height = height
def draw(self):
c = self.canv
# cream background
c.setFillColor(PAPER_BG)
c.rect(0, 0, self.width, self.height, fill=1, stroke=0)
# ruled lines every 7mm
c.setStrokeColor(LINE_COLOR)
c.setLineWidth(0.3)
y = self.height - 20
while y > 20:
c.line(0, y, self.width, y)
y -= 7 * mm
class PageBG:
"""Mixin to add cream background + ruled lines on every page."""
pass
def add_page_background(canvas, doc):
canvas.saveState()
W, H = A4
canvas.setFillColor(PAPER_BG)
canvas.rect(0, 0, W, H, fill=1, stroke=0)
# ruled lines
canvas.setStrokeColor(LINE_COLOR)
canvas.setLineWidth(0.3)
y = H - 18 * mm
while y > 15 * mm:
canvas.line(15*mm, y, W - 12*mm, y)
y -= 7.2 * mm
# left margin red line
canvas.setStrokeColor(colors.HexColor("#e8a0a0"))
canvas.setLineWidth(0.8)
canvas.line(18*mm, H - 10*mm, 18*mm, 12*mm)
# page number at bottom
canvas.setFont('Kalam', 9)
canvas.setFillColor(colors.HexColor("#888888"))
canvas.drawCentredString(W / 2, 8*mm, f"— {doc.page} —")
canvas.restoreState()
# ─── Build PDF story ─────────────────────────────────────────────────────────
def build_story(pages_text, styles):
story = []
used_images = set() # track which image keywords already shown
# Cover page
story.append(Spacer(1, 30*mm))
story.append(Paragraph("✦ Eponyms ✦", styles['cover_title']))
story.append(Spacer(1, 4*mm))
story.append(Paragraph("Surgery Quick Notes", styles['cover_sub']))
story.append(Paragraph("Bailey & Love · MBBS + INI-CET High-Yield", styles['cover_sub']))
story.append(Spacer(1, 6*mm))
story.append(HRFlowable(width="70%", thickness=1.5, color=SUBHEAD_COLOR, spaceAfter=6))
story.append(Paragraph("Named Signs · Classifications · Scoring Systems · Imaging", styles['cover_sub']))
story.append(Spacer(1, 60*mm))
from reportlab.platypus import PageBreak
story.append(PageBreak())
current_section_keyword = None
for page_text in pages_text:
lines = page_text.split('\n')
i = 0
while i < len(lines):
raw = lines[i]
kind, text = classify_line(raw)
if kind == 'blank':
story.append(Spacer(1, 2*mm))
i += 1
continue
if kind == 'separator':
story.append(HRFlowable(width="90%", thickness=0.5,
color=SEPARATOR_CLR, spaceAfter=3, spaceBefore=3))
i += 1
continue
if kind == 'chapter':
# Maybe insert an image for the new chapter topic
img_kw = get_image_for_section(text)
if img_kw and img_kw not in used_images and img_kw in IMAGE_MAP:
used_images.add(img_kw)
url, caption = IMAGE_MAP[img_kw]
img_path = download_image(url, img_kw)
if img_path:
try:
img = RLImage(img_path, width=50*mm, height=40*mm, kind='proportional')
story.append(Spacer(1, 4*mm))
story.append(img)
story.append(Paragraph(caption, styles['img_caption']))
except Exception as e:
print(f" [IMG ERR] {img_kw}: {e}")
story.append(Spacer(1, 5*mm))
story.append(Paragraph(f"📘 {text}", styles['chapter']))
story.append(HRFlowable(width="100%", thickness=1.2, color=HEADING_COLOR, spaceAfter=4))
i += 1
continue
if kind == 'section':
# Check if this section suggests an image
img_kw = get_image_for_section(text)
if img_kw and img_kw != current_section_keyword and img_kw not in used_images and img_kw in IMAGE_MAP:
used_images.add(img_kw)
current_section_keyword = img_kw
url, caption = IMAGE_MAP[img_kw]
img_path = download_image(url, img_kw)
if img_path:
try:
img = RLImage(img_path, width=44*mm, height=36*mm, kind='proportional')
story.append(Spacer(1, 3*mm))
story.append(img)
story.append(Paragraph(caption, styles['img_caption']))
except Exception as e:
print(f" [IMG ERR] {img_kw}: {e}")
story.append(Spacer(1, 4*mm))
story.append(Paragraph(f"🔹 {text}", styles['section']))
i += 1
continue
if kind == 'eponym':
# Eponym / named sign / classification name
story.append(Paragraph(f"<b>{text}</b>", styles['eponym']))
i += 1
continue
if kind == 'arrow':
# Arrow definition
clean = text.replace('→', '▸').strip()
story.append(Paragraph(f"<font color='#c0392b'>▸</font> {clean[1:].strip()}", styles['arrow']))
i += 1
continue
if kind == 'bullet':
# Bullet point
bullet_sym = '●' if text.startswith('•') else '–'
clean = re.sub(r'^[•\-·\d\.]\s*', '', text).strip()
story.append(Paragraph(f"<font color='#27ae60'>{bullet_sym}</font> {clean}", styles['bullet']))
i += 1
continue
# Normal text
story.append(Paragraph(text.strip(), styles['normal']))
i += 1
return story
# ─── Main ────────────────────────────────────────────────────────────────────
def main():
print("Extracting text from PDF...")
pages_text = extract_pages()
print(f" {len(pages_text)} pages extracted")
print("Building story elements...")
styles = make_styles()
story = build_story(pages_text, styles)
print(f" {len(story)} story elements")
print("Generating PDF...")
W, H = A4
doc = SimpleDocTemplate(
OUTPUT_PDF,
pagesize=A4,
leftMargin=22*mm,
rightMargin=14*mm,
topMargin=20*mm,
bottomMargin=18*mm,
title="Eponyms – Surgery Handwritten Notes",
author="Bailey & Love",
subject="Surgery Eponyms, Signs, Classifications"
)
doc.build(story, onFirstPage=add_page_background, onLaterPages=add_page_background)
print(f"Done! → {OUTPUT_PDF}")
if __name__ == '__main__':
main()
cd /home/daytona/workspace/eponyms-notes && python3 build_notes.py 2>&1
# Test a few working Wikimedia image URLs - use direct Commons file URLs python3 -c " import urllib.request # Test direct image links urls = [ 'https://upload.wikimedia.org/wikipedia/commons/9/95/Illu_thyroid_parathyroid.jpg', 'https://upload.wikimedia.org/wikipedia/commons/e/e5/Breast_anatomy_normal_scheme.png', 'https://commons.wikimedia.org/wiki/Special:FilePath/Gray1224.png', ] for url in urls: try: req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 (compatible)'}) with urllib.request.urlopen(req, timeout=8) as r: data = r.read(100) print(f'OK ({len(data)} bytes read): {url[:80]}') except Exception as e: print(f'FAIL: {url[:80]} -> {e}') "
# Since external image downloads are blocked, let's generate simple SVG anatomical icons # using Python's built-in drawing - create clean medical icons as PNG using reportlab canvas python3 << 'EOF' import os from reportlab.graphics import renderPM from reportlab.graphics.shapes import Drawing, Ellipse, Rect, Circle, Line, Path, String, Group from reportlab.lib import colors IMG_DIR = "/home/daytona/workspace/eponyms-notes/images" os.makedirs(IMG_DIR, exist_ok=True) def save_drawing(d, name): path = os.path.join(IMG_DIR, f"{name}.png") renderPM.drawToFile(d, path, fmt='PNG', dpi=150) print(f"Saved: {path}") return path # ── Thyroid (butterfly shape) ───────────────────────────────────────────── def make_thyroid(): d = Drawing(160, 110) d.add(Rect(0,0,160,110, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Left lobe d.add(Ellipse(55, 55, 38, 45, fillColor=colors.HexColor("#7ec8a0"), strokeColor=colors.HexColor("#2e7d52"), strokeWidth=1.5)) # Right lobe d.add(Ellipse(105, 55, 38, 45, fillColor=colors.HexColor("#7ec8a0"), strokeColor=colors.HexColor("#2e7d52"), strokeWidth=1.5)) # Isthmus d.add(Rect(68, 46, 24, 18, fillColor=colors.HexColor("#7ec8a0"), strokeColor=colors.HexColor("#2e7d52"), strokeWidth=1)) # Trachea d.add(Rect(73, 10, 14, 38, fillColor=colors.HexColor("#c8e6f5"), strokeColor=colors.HexColor("#4a90b0"), strokeWidth=1)) # Label d.add(String(80, 6, "Thyroid", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#2e7d52"))) return save_drawing(d, "thyroid") # ── Breast ─────────────────────────────────────────────────────────────── def make_breast(): d = Drawing(160, 110) d.add(Rect(0,0,160,110, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(Ellipse(80, 60, 55, 45, fillColor=colors.HexColor("#f4c2a1"), strokeColor=colors.HexColor("#c0784d"), strokeWidth=1.5)) d.add(Circle(80, 60, 8, fillColor=colors.HexColor("#c0784d"), strokeColor=colors.HexColor("#8b4a2b"), strokeWidth=1)) d.add(String(80, 8, "Breast", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#8b4a2b"))) return save_drawing(d, "breast") # ── Liver ──────────────────────────────────────────────────────────────── def make_liver(): d = Drawing(160, 110) d.add(Rect(0,0,160,110, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(Ellipse(78, 62, 65, 42, fillColor=colors.HexColor("#c0392b"), strokeColor=colors.HexColor("#7b1a11"), strokeWidth=1.5)) # Right lobe larger d.add(Ellipse(58, 65, 45, 38, fillColor=colors.HexColor("#e74c3c"), strokeColor=colors.HexColor("#7b1a11"), strokeWidth=1)) d.add(String(78, 8, "Liver", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#7b1a11"))) return save_drawing(d, "liver") # ── Kidney ─────────────────────────────────────────────────────────────── def make_kidney(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Bean shape - left kidney d.add(Ellipse(55, 65, 32, 46, fillColor=colors.HexColor("#e8a87c"), strokeColor=colors.HexColor("#b5651d"), strokeWidth=1.5)) d.add(Ellipse(67, 65, 20, 30, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Right kidney d.add(Ellipse(105, 65, 32, 46, fillColor=colors.HexColor("#e8a87c"), strokeColor=colors.HexColor("#b5651d"), strokeWidth=1.5)) d.add(Ellipse(93, 65, 20, 30, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(String(80, 8, "Kidneys", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#b5651d"))) return save_drawing(d, "kidney") # ── Stomach / Gastric ──────────────────────────────────────────────────── def make_stomach(): d = Drawing(160, 110) d.add(Rect(0,0,160,110, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(Ellipse(78, 58, 60, 42, fillColor=colors.HexColor("#f7dc6f"), strokeColor=colors.HexColor("#9a7d0a"), strokeWidth=1.5)) d.add(String(78, 8, "Stomach", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#9a7d0a"))) return save_drawing(d, "stomach") # ── Colon ──────────────────────────────────────────────────────────────── def make_colon(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Frame of colon d.add(Rect(20, 20, 120, 85, fillColor=None, strokeColor=colors.HexColor("#8e6b3e"), strokeWidth=3, rx=20, ry=20)) d.add(Circle(80, 62, 30, fillColor=None, strokeColor=None)) d.add(String(80, 8, "Large Intestine", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#8e6b3e"))) return save_drawing(d, "colon") # ── Pancreas ───────────────────────────────────────────────────────────── def make_pancreas(): d = Drawing(180, 90) d.add(Rect(0,0,180,90, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(Ellipse(90, 50, 80, 25, fillColor=colors.HexColor("#f0b27a"), strokeColor=colors.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(String(90, 8, "Pancreas", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#ca6f1e"))) return save_drawing(d, "pancreas") # ── Gallbladder / Biliary ──────────────────────────────────────────────── def make_gallbladder(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Pear shape d.add(Ellipse(80, 52, 28, 42, fillColor=colors.HexColor("#a9cce3"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1.5)) d.add(Ellipse(80, 84, 18, 14, fillColor=colors.HexColor("#a9cce3"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1)) # Duct d.add(Line(80, 98, 80, 108, strokeColor=colors.HexColor("#1a5276"), strokeWidth=2)) d.add(String(80, 6, "Gallbladder", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#1a5276"))) return save_drawing(d, "gallbladder") # ── Lung / Thorax ──────────────────────────────────────────────────────── def make_lung(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Left lung d.add(Ellipse(48, 65, 30, 48, fillColor=colors.HexColor("#85c1e9"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1.5)) # Right lung d.add(Ellipse(112, 65, 30, 48, fillColor=colors.HexColor("#85c1e9"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1.5)) # Trachea d.add(Rect(73, 10, 14, 40, fillColor=colors.HexColor("#c8e6f5"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1)) d.add(String(80, 6, "Lungs", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#1a5276"))) return save_drawing(d, "lung") # ── Spleen ─────────────────────────────────────────────────────────────── def make_spleen(): d = Drawing(160, 110) d.add(Rect(0,0,160,110, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(Ellipse(80, 58, 50, 38, fillColor=colors.HexColor("#8e44ad"), strokeColor=colors.HexColor("#5b2c6f"), strokeWidth=1.5)) d.add(String(80, 8, "Spleen", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#5b2c6f"))) return save_drawing(d, "spleen") # ── Skin / Wound / Burn ────────────────────────────────────────────────── def make_skin(): d = Drawing(160, 110) d.add(Rect(0,0,160,110, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Skin layers d.add(Rect(10, 75, 140, 20, fillColor=colors.HexColor("#f4d03f"), strokeColor=colors.HexColor("#9a7d0a"), strokeWidth=1)) # Epidermis d.add(Rect(10, 50, 140, 25, fillColor=colors.HexColor("#f0b27a"), strokeColor=colors.HexColor("#ca6f1e"), strokeWidth=1)) # Dermis d.add(Rect(10, 20, 140, 30, fillColor=colors.HexColor("#fad7a0"), strokeColor=colors.HexColor("#b7770d"), strokeWidth=1)) # Subcut # Labels d.add(String(80, 88, "Epidermis", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=colors.HexColor("#333"))) d.add(String(80, 65, "Dermis", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=colors.HexColor("#333"))) d.add(String(80, 36, "Subcutaneous", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=colors.HexColor("#333"))) d.add(String(80, 8, "Skin Layers", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#9a7d0a"))) return save_drawing(d, "skin") # ── Appendix ───────────────────────────────────────────────────────────── def make_appendix(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Cecum d.add(Ellipse(75, 85, 35, 30, fillColor=colors.HexColor("#f0b27a"), strokeColor=colors.HexColor("#ca6f1e"), strokeWidth=1.5)) # Appendix d.add(Ellipse(88, 45, 10, 40, fillColor=colors.HexColor("#e74c3c"), strokeColor=colors.HexColor("#922b21"), strokeWidth=1.5)) d.add(String(75, 8, "Appendix", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#922b21"))) return save_drawing(d, "appendix") # ── Neck anatomy ───────────────────────────────────────────────────────── def make_neck(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Neck outline d.add(Rect(50, 15, 60, 95, fillColor=colors.HexColor("#f4d03f"), strokeColor=colors.HexColor("#9a7d0a"), strokeWidth=1.5, rx=15, ry=15)) # Trachea d.add(Rect(68, 30, 24, 70, fillColor=colors.HexColor("#c8e6f5"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1)) # Thyroid d.add(Ellipse(60, 68, 15, 20, fillColor=colors.HexColor("#7ec8a0"), strokeColor=colors.HexColor("#2e7d52"), strokeWidth=1)) d.add(Ellipse(100, 68, 15, 20, fillColor=colors.HexColor("#7ec8a0"), strokeColor=colors.HexColor("#2e7d52"), strokeWidth=1)) d.add(String(80, 6, "Neck Anatomy", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#1a5276"))) return save_drawing(d, "neck") # ── Vascular / Aorta ───────────────────────────────────────────────────── def make_vascular(): d = Drawing(160, 130) d.add(Rect(0,0,160,130, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Aorta d.add(Rect(70, 15, 20, 100, fillColor=colors.HexColor("#e74c3c"), strokeColor=colors.HexColor("#922b21"), strokeWidth=1.5)) # Branches d.add(Line(80, 95, 30, 85, strokeColor=colors.HexColor("#e74c3c"), strokeWidth=2)) d.add(Line(80, 95, 130, 85, strokeColor=colors.HexColor("#e74c3c"), strokeWidth=2)) d.add(Line(80, 70, 25, 60, strokeColor=colors.HexColor("#e74c3c"), strokeWidth=2)) d.add(Line(80, 70, 135, 60, strokeColor=colors.HexColor("#e74c3c"), strokeWidth=2)) d.add(String(80, 6, "Vascular Anatomy", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#922b21"))) return save_drawing(d, "vascular") # ── Hernia / Inguinal ──────────────────────────────────────────────────── def make_hernia(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Abdominal wall d.add(Rect(10, 60, 140, 20, fillColor=colors.HexColor("#85c1e9"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1.5)) # Hernia bulge d.add(Ellipse(80, 42, 28, 22, fillColor=colors.HexColor("#f0b27a"), strokeColor=colors.HexColor("#ca6f1e"), strokeWidth=1.5)) # Defect in wall d.add(Rect(64, 58, 32, 24, fillColor=colors.HexColor("#f0b27a"), strokeColor=None)) d.add(String(80, 6, "Hernia", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#ca6f1e"))) return save_drawing(d, "hernia") # ── Anal / Rectal ──────────────────────────────────────────────────────── def make_anal(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Rectum d.add(Rect(50, 30, 60, 60, fillColor=colors.HexColor("#f0b27a"), strokeColor=colors.HexColor("#ca6f1e"), strokeWidth=1.5, rx=10, ry=10)) # Anal canal d.add(Rect(62, 88, 36, 22, fillColor=colors.HexColor("#e8a87c"), strokeColor=colors.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(String(80, 6, "Rectum & Anal Canal", textAnchor='middle', fontName='Helvetica', fontSize=8, fillColor=colors.HexColor("#ca6f1e"))) return save_drawing(d, "anal") # ── Bone ───────────────────────────────────────────────────────────────── def make_bone(): d = Drawing(160, 130) d.add(Rect(0,0,160,130, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Long bone d.add(Ellipse(80, 108, 28, 20, fillColor=colors.HexColor("#f5f5dc"), strokeColor=colors.HexColor("#8b7355"), strokeWidth=1.5)) d.add(Rect(70, 30, 20, 80, fillColor=colors.HexColor("#f5f5dc"), strokeColor=colors.HexColor("#8b7355"), strokeWidth=1.5)) d.add(Ellipse(80, 22, 28, 20, fillColor=colors.HexColor("#f5f5dc"), strokeColor=colors.HexColor("#8b7355"), strokeWidth=1.5)) # Medullary canal d.add(Rect(75, 30, 10, 80, fillColor=colors.HexColor("#ffe4b5"), strokeColor=None)) d.add(String(80, 6, "Long Bone", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#8b7355"))) return save_drawing(d, "bone") # ── Brain ──────────────────────────────────────────────────────────────── def make_brain(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(Ellipse(80, 65, 65, 50, fillColor=colors.HexColor("#ffcba4"), strokeColor=colors.HexColor("#b5651d"), strokeWidth=1.5)) # Central sulcus line d.add(Line(80, 110, 80, 20, strokeColor=colors.HexColor("#b5651d"), strokeWidth=1)) d.add(String(80, 6, "Brain", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#b5651d"))) return save_drawing(d, "brain") # ── Salivary gland ────────────────────────────────────────────────────── def make_salivary(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Parotid d.add(Ellipse(80, 75, 50, 35, fillColor=colors.HexColor("#a9cce3"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1.5)) # Submandibular d.add(Ellipse(55, 45, 25, 18, fillColor=colors.HexColor("#85c1e9"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1)) d.add(Ellipse(105, 45, 25, 18, fillColor=colors.HexColor("#85c1e9"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1)) d.add(String(80, 6, "Salivary Glands", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#1a5276"))) return save_drawing(d, "salivary") # ── Generic lymph ──────────────────────────────────────────────────────── def make_lymph(): d = Drawing(160, 120) d.add(Rect(0,0,160,120, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) # Lymph nodes chain for y in [95, 75, 55, 35]: d.add(Ellipse(80, y, 18, 12, fillColor=colors.HexColor("#a9cce3"), strokeColor=colors.HexColor("#1a5276"), strokeWidth=1.5)) # Connecting lines for y in [83, 63, 43]: d.add(Line(80, y, 80, y-10, strokeColor=colors.HexColor("#1a5276"), strokeWidth=1)) d.add(String(80, 6, "Lymph Nodes", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#1a5276"))) return save_drawing(d, "lymph") # ── Esophagus ──────────────────────────────────────────────────────────── def make_esophagus(): d = Drawing(160, 140) d.add(Rect(0,0,160,140, fillColor=colors.HexColor("#f5f0e8"), strokeColor=None)) d.add(Rect(65, 15, 30, 115, fillColor=colors.HexColor("#f4c2a1"), strokeColor=colors.HexColor("#c0784d"), strokeWidth=1.5, rx=10, ry=10)) d.add(String(80, 6, "Oesophagus", textAnchor='middle', fontName='Helvetica', fontSize=9, fillColor=colors.HexColor("#c0784d"))) return save_drawing(d, "esophagus") # Run all make_thyroid() make_breast() make_liver() make_kidney() make_stomach() make_colon() make_pancreas() make_gallbladder() make_lung() make_spleen() make_skin() make_appendix() make_neck() make_vascular() make_hernia() make_anal() make_bone() make_brain() make_salivary() make_lymph() make_esophagus() print("All images generated!") EOF
# Use svg2rlg + renderSVG approach, or draw directly onto PDF canvas using reportlab drawings # Let's draw images directly INSIDE the PDF using ReportLab SVG/Drawing rendered to PDF canvas # We can use renderSVG or embed drawings as flowable directly - no PNG needed python3 -c " from reportlab.graphics.shapes import Drawing, Ellipse, Rect, Circle, Line, String from reportlab.graphics import renderSVG d = Drawing(100, 80) d.add(Ellipse(50, 40, 40, 30)) # Try rendering to SVG (this should work) renderSVG.drawToFile(d, '/tmp/test.svg') print('SVG render OK') # Check if we can use svglib import svglib; print('svglib OK') " 2>&1 | head -20
~/eponyms-notes/build_notes.py
#!/usr/bin/env python3
"""
Convert Eponyms.pdf into a handwritten medical-notes style PDF
with contextual anatomical diagrams drawn via ReportLab at topic changes.
"""
import pdfplumber
import re
import os
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, HRFlowable,
PageBreak, Table, TableStyle
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.graphics.shapes import (
Drawing, Ellipse, Rect, Circle, Line, String, Group, Path
)
from reportlab.graphics import renderPDF
from reportlab.graphics.renderPDF import GraphicsFlowable
# ─── Paths ──────────────────────────────────────────────────────────────────
INPUT_PDF = "/home/daytona/workspace/attachments/9f0a77fd-a844-45c4-9678-be9b40b85ded/Eponyms.pdf"
OUTPUT_PDF = "/home/daytona/workspace/eponyms-notes/Eponyms_HandwrittenNotes.pdf"
FONTS_DIR = "/home/daytona/workspace/eponyms-notes/fonts"
# ─── Register Fonts ─────────────────────────────────────────────────────────
pdfmetrics.registerFont(TTFont('Kalam', f"{FONTS_DIR}/Kalam-Regular.ttf"))
pdfmetrics.registerFont(TTFont('KalamBold', f"{FONTS_DIR}/Kalam-Bold.ttf"))
pdfmetrics.registerFont(TTFont('Patrick', f"{FONTS_DIR}/PatrickHand-Regular.ttf"))
# ─── Colours ────────────────────────────────────────────────────────────────
PAPER_BG = colors.HexColor("#FFF9F0")
LINE_COLOR = colors.HexColor("#B0C4DE")
HEADING_COLOR = colors.HexColor("#1a3a5c")
SUBHEAD_COLOR = colors.HexColor("#2e6e9e")
SEPARATOR_CLR = colors.HexColor("#aaaaaa")
TITLE_COLOR = colors.HexColor("#8B0000")
# ─── Paragraph Styles ───────────────────────────────────────────────────────
def make_styles():
S = {}
S['cover_title'] = ParagraphStyle(
'cover_title', fontName='KalamBold', fontSize=38,
textColor=TITLE_COLOR, leading=46, alignment=TA_CENTER, spaceAfter=6
)
S['cover_sub'] = ParagraphStyle(
'cover_sub', fontName='Kalam', fontSize=13,
textColor=SUBHEAD_COLOR, leading=18, alignment=TA_CENTER, spaceAfter=4
)
S['chapter'] = ParagraphStyle(
'chapter', fontName='KalamBold', fontSize=18,
textColor=HEADING_COLOR, leading=24, spaceBefore=12, spaceAfter=3
)
S['section'] = ParagraphStyle(
'section', fontName='KalamBold', fontSize=13,
textColor=SUBHEAD_COLOR, leading=19, spaceBefore=9, spaceAfter=2
)
S['eponym'] = ParagraphStyle(
'eponym', fontName='KalamBold', fontSize=11,
textColor=HEADING_COLOR, leading=16, spaceBefore=5, spaceAfter=1
)
S['arrow'] = ParagraphStyle(
'arrow', fontName='Kalam', fontSize=11,
textColor=colors.HexColor("#333333"), leading=15, spaceBefore=0, spaceAfter=1,
leftIndent=12
)
S['bullet'] = ParagraphStyle(
'bullet', fontName='Kalam', fontSize=11,
textColor=colors.HexColor("#333333"), leading=15, spaceBefore=0, spaceAfter=1,
leftIndent=20
)
S['normal'] = ParagraphStyle(
'normal', fontName='Kalam', fontSize=11,
textColor=colors.HexColor("#222222"), leading=16, spaceBefore=1, spaceAfter=1
)
S['img_caption'] = ParagraphStyle(
'img_caption', fontName='Kalam', fontSize=9,
textColor=colors.HexColor("#555555"), leading=12, spaceBefore=1, spaceAfter=5,
alignment=TA_CENTER
)
return S
# ─── Anatomical Diagram Drawings ─────────────────────────────────────────────
C = colors # alias
def _base(w, h):
d = Drawing(w, h)
d.add(Rect(0, 0, w, h, fillColor=C.HexColor("#fffdf5"), strokeColor=None))
return d
def _label(d, w, text, color="#555555"):
d.add(String(w/2, 6, text, textAnchor='middle',
fontName='Helvetica', fontSize=8, fillColor=C.HexColor(color)))
def draw_thyroid():
d = _base(170, 120)
# trachea
d.add(Rect(75, 12, 20, 55, fillColor=C.HexColor("#c8e6f5"),
strokeColor=C.HexColor("#4a90b0"), strokeWidth=1.2, rx=4))
# left lobe
d.add(Ellipse(52, 68, 40, 48, fillColor=C.HexColor("#7ec8a0"),
strokeColor=C.HexColor("#2e7d52"), strokeWidth=1.5))
# right lobe
d.add(Ellipse(118, 68, 40, 48, fillColor=C.HexColor("#7ec8a0"),
strokeColor=C.HexColor("#2e7d52"), strokeWidth=1.5))
# isthmus
d.add(Rect(70, 48, 30, 20, fillColor=C.HexColor("#7ec8a0"),
strokeColor=C.HexColor("#2e7d52"), strokeWidth=1))
# parathyroid dots
for x, y in [(38, 82), (66, 82), (104, 82), (132, 82)]:
d.add(Circle(x, y, 4, fillColor=C.HexColor("#f5a623"),
strokeColor=C.HexColor("#c0783b"), strokeWidth=1))
_label(d, 170, "Thyroid & Parathyroid", "#2e7d52")
return d
def draw_breast():
d = _base(170, 120)
d.add(Ellipse(85, 65, 62, 48, fillColor=C.HexColor("#f4c2a1"),
strokeColor=C.HexColor("#c0784d"), strokeWidth=1.8))
d.add(Circle(85, 65, 9, fillColor=C.HexColor("#c0784d"),
strokeColor=C.HexColor("#8b4a2b"), strokeWidth=1))
# Cooper's ligaments lines
for angle_x, angle_y in [(-30, 25), (30, 25), (-50, 0), (50, 0)]:
d.add(Line(85, 65, 85+angle_x, 65+angle_y,
strokeColor=C.HexColor("#b07040"), strokeWidth=0.7))
_label(d, 170, "Breast Anatomy", "#8b4a2b")
return d
def draw_liver():
d = _base(170, 115)
# right lobe
d.add(Ellipse(62, 68, 55, 42, fillColor=C.HexColor("#e74c3c"),
strokeColor=C.HexColor("#7b1a11"), strokeWidth=1.5))
# left lobe
d.add(Ellipse(122, 70, 38, 28, fillColor=C.HexColor("#c0392b"),
strokeColor=C.HexColor("#7b1a11"), strokeWidth=1.5))
# falciform ligament
d.add(Line(88, 28, 88, 68, strokeColor=C.HexColor("#7b1a11"), strokeWidth=1))
# gallbladder
d.add(Ellipse(75, 38, 12, 18, fillColor=C.HexColor("#85c1e9"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
_label(d, 170, "Liver (Right & Left Lobes)", "#7b1a11")
return d
def draw_kidney():
d = _base(170, 130)
for cx in [48, 122]:
flip = -1 if cx < 85 else 1
d.add(Ellipse(cx, 68, 34, 50, fillColor=C.HexColor("#e8a87c"),
strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5))
d.add(Ellipse(cx + flip*10, 68, 20, 34,
fillColor=C.HexColor("#fffdf5"), strokeColor=None))
# pelvis
d.add(Ellipse(cx + flip*6, 68, 10, 18, fillColor=C.HexColor("#f5a623"),
strokeColor=C.HexColor("#b5651d"), strokeWidth=0.8))
# ureter stubs
d.add(Line(58, 100, 58, 118, strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5))
d.add(Line(112, 100, 112, 118, strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5))
_label(d, 170, "Kidneys & Ureters", "#b5651d")
return d
def draw_stomach():
d = _base(170, 120)
# J-shape stomach
d.add(Ellipse(82, 68, 68, 46, fillColor=C.HexColor("#f7dc6f"),
strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1.5))
# Greater curvature bump
d.add(Ellipse(58, 50, 28, 26, fillColor=C.HexColor("#f7dc6f"),
strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1))
# Pylorus
d.add(Rect(128, 58, 16, 20, fillColor=C.HexColor("#f0ca2f"),
strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1, rx=6))
_label(d, 170, "Stomach Anatomy", "#9a7d0a")
return d
def draw_pancreas():
d = _base(200, 90)
d.add(Rect(0,0,200,90, fillColor=C.HexColor("#fffdf5"), strokeColor=None))
# Head
d.add(Ellipse(42, 48, 38, 32, fillColor=C.HexColor("#f0b27a"),
strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5))
# Body
d.add(Rect(75, 34, 60, 28, fillColor=C.HexColor("#f0b27a"),
strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1))
# Tail
d.add(Ellipse(162, 52, 25, 20, fillColor=C.HexColor("#f0b27a"),
strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5))
# Pancreatic duct
d.add(Line(18, 48, 180, 48, strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5))
String(100, 6, "Pancreas – Head, Body, Tail", textAnchor='middle',
fontName='Helvetica', fontSize=8, fillColor=C.HexColor("#ca6f1e"))
d.add(String(100, 6, "Pancreas – Head, Body, Tail",
textAnchor='middle', fontName='Helvetica', fontSize=8,
fillColor=C.HexColor("#ca6f1e")))
return d
def draw_gallbladder():
d = _base(170, 130)
# Liver outline
d.add(Ellipse(68, 105, 55, 22, fillColor=C.HexColor("#e74c3c"),
strokeColor=C.HexColor("#7b1a11"), strokeWidth=1))
# Gallbladder pear shape
d.add(Ellipse(82, 72, 26, 38, fillColor=C.HexColor("#a9cce3"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
d.add(Ellipse(82, 38, 16, 16, fillColor=C.HexColor("#a9cce3"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
# CBD
d.add(Line(90, 28, 105, 12, strokeColor=C.HexColor("#1a5276"), strokeWidth=2))
_label(d, 170, "Gallbladder & Biliary System", "#1a5276")
return d
def draw_colon():
d = _base(200, 140)
d.add(Rect(0,0,200,140, fillColor=C.HexColor("#fffdf5"), strokeColor=None))
col = C.HexColor("#8e6b3e")
sw = 7
# ascending
d.add(Line(40, 18, 40, 108, strokeColor=col, strokeWidth=sw))
# transverse
d.add(Line(40, 108, 160, 108, strokeColor=col, strokeWidth=sw))
# descending
d.add(Line(160, 108, 160, 18, strokeColor=col, strokeWidth=sw))
# sigmoid
d.add(Ellipse(125, 30, 32, 18, fillColor=None, strokeColor=col, strokeWidth=sw))
# cecum + appendix
d.add(Ellipse(40, 28, 16, 16, fillColor=C.HexColor("#f0b27a"),
strokeColor=col, strokeWidth=2))
d.add(Ellipse(30, 14, 6, 14, fillColor=C.HexColor("#e74c3c"),
strokeColor=col, strokeWidth=1.5))
_label(d, 200, "Large Intestine / Colon", "#8e6b3e")
return d
def draw_lung():
d = _base(170, 130)
# Left lung
d.add(Ellipse(48, 70, 32, 52, fillColor=C.HexColor("#85c1e9"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
d.add(Line(45, 44, 52, 44, strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
# Right lung
d.add(Ellipse(122, 70, 36, 52, fillColor=C.HexColor("#85c1e9"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
d.add(Line(116, 35, 126, 35, strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
d.add(Line(116, 48, 126, 48, strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
# Trachea
d.add(Rect(74, 14, 22, 48, fillColor=C.HexColor("#c8e6f5"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1.2, rx=4))
# Carina
d.add(Line(74, 36, 48, 24, strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
d.add(Line(96, 36, 122, 24, strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
_label(d, 170, "Lungs & Tracheobronchial Tree", "#1a5276")
return d
def draw_spleen():
d = _base(170, 115)
d.add(Ellipse(85, 62, 62, 46, fillColor=C.HexColor("#9b59b6"),
strokeColor=C.HexColor("#5b2c6f"), strokeWidth=1.5))
# Notch
d.add(Line(35, 75, 45, 58, strokeColor=C.HexColor("#5b2c6f"), strokeWidth=2))
# Hilum
d.add(Ellipse(112, 62, 8, 18, fillColor=C.HexColor("#fffdf5"), strokeColor=None))
_label(d, 170, "Spleen", "#5b2c6f")
return d
def draw_appendix():
d = _base(170, 140)
# Cecum
d.add(Ellipse(78, 95, 42, 38, fillColor=C.HexColor("#f0b27a"),
strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5))
# Terminal ileum
d.add(Rect(15, 85, 45, 16, fillColor=C.HexColor("#f7dc6f"),
strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1, rx=6))
# Appendix
d.add(Ellipse(85, 50, 10, 46, fillColor=C.HexColor("#e74c3c"),
strokeColor=C.HexColor("#922b21"), strokeWidth=1.5))
# McBurney point marker
d.add(String(108, 60, "McBurney's Pt", fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#555")))
d.add(Line(108, 58, 90, 52, strokeColor=C.HexColor("#e74c3c"), strokeWidth=0.8))
_label(d, 170, "Appendix & Ileocecal Region", "#922b21")
return d
def draw_skin():
d = _base(180, 120)
d.add(Rect(0,0,180,120, fillColor=C.HexColor("#fffdf5"), strokeColor=None))
layers = [
(80, 16, C.HexColor("#f4d03f"), C.HexColor("#9a7d0a"), "Epidermis"),
(70, 24, C.HexColor("#f0b27a"), C.HexColor("#ca6f1e"), "Dermis"),
(60, 32, C.HexColor("#fad7a0"), C.HexColor("#b7770d"), "Subcutaneous fat"),
]
y = 86
for bw, bh, fc, sc, lbl in layers:
d.add(Rect(10, y, 160, bh, fillColor=fc, strokeColor=sc, strokeWidth=1))
d.add(String(20, y + bh/2 - 4, lbl, fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#333")))
y -= bh + 2
_label(d, 180, "Skin Layers", "#9a7d0a")
return d
def draw_vascular():
d = _base(170, 140)
col = C.HexColor("#e74c3c")
# Descending aorta
d.add(Rect(75, 18, 20, 112, fillColor=col, strokeColor=C.HexColor("#922b21"),
strokeWidth=1.5, rx=5))
# Aortic arch
d.add(Ellipse(68, 128, 28, 15, fillColor=col, strokeColor=C.HexColor("#922b21"),
strokeWidth=1.5))
# Branches: celiac, SMA, renals, iliac
for y_val, x_right in [(108, 40), (92, 35), (74, 38), (58, 42)]:
d.add(Line(75, y_val, x_right, y_val, strokeColor=col, strokeWidth=2))
d.add(Line(95, y_val, 170-x_right, y_val, strokeColor=col, strokeWidth=2))
# Iliac bifurcation
d.add(Line(85, 28, 55, 16, strokeColor=col, strokeWidth=2.5))
d.add(Line(85, 28, 115, 16, strokeColor=col, strokeWidth=2.5))
_label(d, 170, "Aorta & Major Branches", "#922b21")
return d
def draw_hernia():
d = _base(170, 130)
# Abdominal wall (blue layer)
d.add(Rect(10, 62, 150, 24, fillColor=C.HexColor("#85c1e9"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
# Defect opening
d.add(Rect(70, 62, 30, 24, fillColor=C.HexColor("#fffdf5"), strokeColor=None))
# Hernia sac bulging down
d.add(Ellipse(85, 48, 30, 22, fillColor=C.HexColor("#f0b27a"),
strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5))
# Peritoneum above
d.add(Rect(10, 84, 150, 8, fillColor=C.HexColor("#f7dc6f"),
strokeColor=C.HexColor("#9a7d0a"), strokeWidth=0.8))
d.add(String(100, 96, "Defect", fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#555")))
d.add(String(100, 48, "Sac", fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#555")))
_label(d, 170, "Hernia – Abdominal Wall Defect", "#ca6f1e")
return d
def draw_anal():
d = _base(170, 140)
# Rectum
d.add(Rect(52, 64, 66, 64, fillColor=C.HexColor("#f0b27a"),
strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5, rx=10))
# Anal canal
d.add(Rect(62, 24, 46, 44, fillColor=C.HexColor("#e8a87c"),
strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5))
# External sphincter
d.add(Ellipse(85, 28, 36, 10, fillColor=None, strokeColor=C.HexColor("#922b21"),
strokeWidth=2))
# Dentate line
d.add(Line(62, 48, 108, 48, strokeColor=C.HexColor("#1a5276"), strokeWidth=1.2))
d.add(String(112, 44, "Dentate line", fontName='Helvetica', fontSize=6,
fillColor=C.HexColor("#1a5276")))
_label(d, 170, "Rectum & Anal Canal", "#ca6f1e")
return d
def draw_neck():
d = _base(170, 140)
# Neck outline
d.add(Rect(52, 18, 66, 112, fillColor=C.HexColor("#fde9d9"),
strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5, rx=18))
# Trachea
d.add(Rect(72, 28, 26, 80, fillColor=C.HexColor("#c8e6f5"),
strokeColor=C.HexColor("#4a90b0"), strokeWidth=1.2, rx=5))
# Thyroid
d.add(Ellipse(64, 80, 16, 24, fillColor=C.HexColor("#7ec8a0"),
strokeColor=C.HexColor("#2e7d52"), strokeWidth=1))
d.add(Ellipse(106, 80, 16, 24, fillColor=C.HexColor("#7ec8a0"),
strokeColor=C.HexColor("#2e7d52"), strokeWidth=1))
# Carotid stubs
d.add(Line(55, 108, 55, 128, strokeColor=C.HexColor("#e74c3c"), strokeWidth=3))
d.add(Line(115, 108, 115, 128, strokeColor=C.HexColor("#e74c3c"), strokeWidth=3))
_label(d, 170, "Neck Anatomy", "#1a5276")
return d
def draw_lymph():
d = _base(130, 140)
col_node = C.HexColor("#a9cce3")
col_edge = C.HexColor("#1a5276")
col_vessel = C.HexColor("#5dade2")
xs = [65, 65, 65, 48, 82, 48, 82]
ys = [120, 98, 76, 56, 56, 36, 36]
# edges
for i in range(len(xs)-1):
d.add(Line(xs[i], ys[i], xs[i+1], ys[i+1],
strokeColor=col_vessel, strokeWidth=1))
for x, y in zip(xs, ys):
d.add(Ellipse(x, y, 13, 9, fillColor=col_node,
strokeColor=col_edge, strokeWidth=1.2))
_label(d, 130, "Lymph Node Chain", "#1a5276")
return d
def draw_bone():
d = _base(130, 150)
# Epiphysis top
d.add(Ellipse(65, 132, 36, 16, fillColor=C.HexColor("#f5f5dc"),
strokeColor=C.HexColor("#8b7355"), strokeWidth=1.5))
# Diaphysis
d.add(Rect(52, 30, 26, 104, fillColor=C.HexColor("#f5f5dc"),
strokeColor=C.HexColor("#8b7355"), strokeWidth=1.5))
# Medullary canal
d.add(Rect(58, 40, 14, 84, fillColor=C.HexColor("#ffe4b5"), strokeColor=None))
# Periosteum line
d.add(Rect(49, 30, 32, 104, fillColor=None,
strokeColor=C.HexColor("#aaa"), strokeWidth=0.7))
# Epiphysis bottom
d.add(Ellipse(65, 18, 36, 16, fillColor=C.HexColor("#f5f5dc"),
strokeColor=C.HexColor("#8b7355"), strokeWidth=1.5))
_label(d, 130, "Long Bone Structure", "#8b7355")
return d
def draw_brain():
d = _base(180, 130)
d.add(Ellipse(90, 72, 78, 56, fillColor=C.HexColor("#ffcba4"),
strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5))
# Lobes divisions
d.add(Line(90, 124, 90, 20, strokeColor=C.HexColor("#b5651d"), strokeWidth=1))
d.add(Line(30, 90, 150, 90, strokeColor=C.HexColor("#b5651d"), strokeWidth=0.8))
d.add(Line(60, 124, 40, 80, strokeColor=C.HexColor("#b5651d"), strokeWidth=0.8))
# Cerebellum
d.add(Ellipse(90, 28, 48, 20, fillColor=C.HexColor("#f4d03f"),
strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1))
# Labels
d.add(String(60, 105, "Frontal", fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#555")))
d.add(String(120, 105, "Parietal", fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#555")))
d.add(String(60, 68, "Temporal", fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#555")))
d.add(String(120, 68, "Occipital", fontName='Helvetica', fontSize=7,
fillColor=C.HexColor("#555")))
_label(d, 180, "Brain Lobes", "#b5651d")
return d
def draw_salivary():
d = _base(180, 130)
# Parotid
d.add(Ellipse(42, 72, 36, 46, fillColor=C.HexColor("#a9cce3"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
d.add(String(42, 24, "Parotid", textAnchor='middle', fontName='Helvetica',
fontSize=7, fillColor=C.HexColor("#1a5276")))
d.add(Ellipse(138, 72, 36, 46, fillColor=C.HexColor("#a9cce3"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5))
d.add(String(138, 24, "Parotid", textAnchor='middle', fontName='Helvetica',
fontSize=7, fillColor=C.HexColor("#1a5276")))
# Submandibular
d.add(Ellipse(62, 95, 22, 16, fillColor=C.HexColor("#85c1e9"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
d.add(Ellipse(118, 95, 22, 16, fillColor=C.HexColor("#85c1e9"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
# Sublingual
d.add(Ellipse(90, 105, 18, 10, fillColor=C.HexColor("#d6eaf8"),
strokeColor=C.HexColor("#1a5276"), strokeWidth=1))
_label(d, 180, "Salivary Glands", "#1a5276")
return d
def draw_esophagus():
d = _base(130, 160)
d.add(Rect(48, 18, 34, 132, fillColor=C.HexColor("#f4c2a1"),
strokeColor=C.HexColor("#c0784d"), strokeWidth=1.5, rx=10))
# 3 constrictions
for yc, lbl in [(138, "Cricopharyngeus"), (92, "Aortic arch"), (24, "Diaphragm")]:
d.add(Rect(54, yc-4, 22, 8, fillColor=C.HexColor("#f5f0e8"),
strokeColor=C.HexColor("#c0784d"), strokeWidth=0.8))
d.add(String(80, yc-2, lbl, fontName='Helvetica', fontSize=6,
fillColor=C.HexColor("#555")))
_label(d, 130, "Oesophagus (3 constrictions)", "#c0784d")
return d
# ─── Map section keywords → drawing functions + captions ──────────────────
SECTION_DRAWINGS = {
"THYROID": (draw_thyroid, "Thyroid & Parathyroid"),
"PARATHYROID": (draw_thyroid, "Parathyroid Glands"),
"BREAST": (draw_breast, "Breast Anatomy"),
"LIVER": (draw_liver, "Liver Anatomy"),
"HEPAT": (draw_liver, "Hepatic Anatomy"),
"PANCREA": (draw_pancreas, "Pancreas"),
"APPENDIX": (draw_appendix, "Appendix"),
"APPEND": (draw_appendix, "Appendix"),
"COLON": (draw_colon, "Large Intestine"),
"COLORECT": (draw_colon, "Colorectal Anatomy"),
"RECTUM": (draw_anal, "Rectum & Anal Canal"),
"ANAL": (draw_anal, "Anal Canal"),
"ANORECTAL": (draw_anal, "Anorectal Anatomy"),
"INGUINAL": (draw_hernia, "Inguinal / Hernia"),
"HERNIA": (draw_hernia, "Hernia Anatomy"),
"STOMACH": (draw_stomach, "Stomach"),
"GASTRIC": (draw_stomach, "Gastric Anatomy"),
"OESOPH": (draw_esophagus, "Oesophagus"),
"ESOPH": (draw_esophagus, "Oesophagus"),
"KIDNEY": (draw_kidney, "Kidneys"),
"RENAL": (draw_kidney, "Renal Anatomy"),
"ADRENAL": (draw_kidney, "Adrenal & Kidney"),
"SPLEEN": (draw_spleen, "Spleen"),
"SPLENIC": (draw_spleen, "Splenic Anatomy"),
"GALLBLADDER": (draw_gallbladder, "Gallbladder"),
"BILIARY": (draw_gallbladder, "Biliary System"),
"BILE": (draw_gallbladder, "Bile Ducts"),
"VASCULAR": (draw_vascular, "Vascular System"),
"AORTA": (draw_vascular, "Aorta"),
"CAROTID": (draw_neck, "Carotid / Neck"),
"NECK": (draw_neck, "Neck Anatomy"),
"PAROTID": (draw_salivary, "Salivary Glands"),
"SALIVARY": (draw_salivary, "Salivary Glands"),
"LUNG": (draw_lung, "Lungs"),
"THORAC": (draw_lung, "Thorax"),
"CHEST": (draw_lung, "Chest / Lungs"),
"WOUND": (draw_skin, "Wound / Skin"),
"SKIN": (draw_skin, "Skin Layers"),
"BURNS": (draw_skin, "Burns / Skin"),
"BURN": (draw_skin, "Burns"),
"LYMPH": (draw_lymph, "Lymph Nodes"),
"BRAIN": (draw_brain, "Brain Lobes"),
"SKULL": (draw_brain, "Skull / Brain"),
"HEAD": (draw_brain, "Head / Brain"),
"BONE": (draw_bone, "Bone Structure"),
"FRACTURE": (draw_bone, "Bone / Fracture"),
"ORTHOPAED": (draw_bone, "Orthopaedics"),
"ORTHOPED": (draw_bone, "Orthopaedics"),
"UROLOGY": (draw_kidney, "Urological Anatomy"),
"UROLOG": (draw_kidney, "Urology"),
"BLADDER": (draw_kidney, "Bladder"),
"PROSTATE": (draw_kidney, "Prostate"),
"TESTIS": (draw_kidney, "Testicular Anatomy"),
"TESTICULAR": (draw_kidney, "Testis"),
"SCROTAL": (draw_kidney, "Scrotal Anatomy"),
"VARICOSE": (draw_vascular, "Venous Anatomy"),
"VEIN": (draw_vascular, "Veins"),
"PERITON": (draw_stomach, "Peritoneal Cavity"),
"SMALL INTESTINE":(draw_colon, "Small Intestine"),
"DUODENUM": (draw_stomach, "Duodenum"),
"HIP": (draw_bone, "Hip Joint"),
"SHOULDER": (draw_bone, "Shoulder Joint"),
"TRAUMA": (draw_bone, "Trauma"),
"LAPAROSCOP": (draw_liver, "Laparoscopy"),
"ENDOSCOP": (draw_esophagus, "Endoscopy"),
"INFECTION": (draw_skin, "Infection / Skin"),
"SEPSIS": (draw_vascular, "Sepsis / Vascular"),
"SHOCK": (draw_vascular, "Shock / Vascular"),
"ONCOLOGY": (draw_lymph, "Surgical Oncology"),
"INCISION": (draw_skin, "Surgical Incision"),
}
def get_drawing_for_section(heading_text):
upper = heading_text.upper()
for kw, (fn, cap) in SECTION_DRAWINGS.items():
if kw in upper:
return fn, cap
return None, None
# ─── Extract text from PDF ───────────────────────────────────────────────────
def extract_pages():
pages = []
with pdfplumber.open(INPUT_PDF) as pdf:
for page in pdf.pages:
text = page.extract_text() or ""
pages.append(text)
return pages
# ─── Line classifier ─────────────────────────────────────────────────────────
def classify_line(line):
s = line.strip()
if not s:
return ('blank', s)
if s in ('⸻', '---') or (len(s) > 2 and len(set(s).difference({'─', '-', '—', '⸻', '–', ' '})) == 0):
return ('separator', s)
if re.match(r'^\d+\.\s+[A-Z][A-Z\s/]+$', s) and len(s) < 80:
return ('chapter', s)
if re.match(r'^[A-Z]\.\s+[A-Z]', s) and len(s) < 80:
return ('section', s)
if s.startswith('→'):
return ('arrow', s)
if s.startswith('•') or re.match(r'^[-·]\s', s):
return ('bullet', s)
if re.match(r'^\d+\.\s+[a-z]', s) or re.match(r'^\d+\.\s+[A-Z][a-z]', s):
return ('bullet', s)
return ('eponym', s)
# ─── Page background callback ────────────────────────────────────────────────
def add_page_background(canvas, doc):
canvas.saveState()
W, H = A4
canvas.setFillColor(PAPER_BG)
canvas.rect(0, 0, W, H, fill=1, stroke=0)
# ruled lines every 7.2mm
canvas.setStrokeColor(LINE_COLOR)
canvas.setLineWidth(0.3)
y = H - 18*mm
while y > 14*mm:
canvas.line(20*mm, y, W - 12*mm, y)
y -= 7.2*mm
# red margin line
canvas.setStrokeColor(colors.HexColor("#e8a0a0"))
canvas.setLineWidth(0.9)
canvas.line(22*mm, H - 10*mm, 22*mm, 12*mm)
# page number
canvas.setFont('Kalam', 9)
canvas.setFillColor(colors.HexColor("#888888"))
canvas.drawCentredString(W/2, 8*mm, f"\u2015 {doc.page} \u2015")
canvas.restoreState()
# ─── Build story ─────────────────────────────────────────────────────────────
def build_story(pages_text, styles):
story = []
used_drawings = set()
# Cover
story.append(Spacer(1, 28*mm))
story.append(Paragraph("✦ Eponyms ✦", styles['cover_title']))
story.append(Spacer(1, 3*mm))
story.append(Paragraph("Surgery Quick Notes", styles['cover_sub']))
story.append(Paragraph("Bailey & Love · MBBS + INI-CET High-Yield", styles['cover_sub']))
story.append(Spacer(1, 5*mm))
story.append(HRFlowable(width="65%", thickness=1.5, color=SUBHEAD_COLOR, spaceAfter=5))
story.append(Paragraph("Named Signs · Classifications · Scoring · Imaging", styles['cover_sub']))
story.append(Spacer(1, 55*mm))
story.append(PageBreak())
for page_text in pages_text:
lines = page_text.split('\n')
for raw in lines:
kind, text = classify_line(raw)
if kind == 'blank':
story.append(Spacer(1, 1.8*mm))
continue
if kind == 'separator':
story.append(HRFlowable(width="88%", thickness=0.5,
color=SEPARATOR_CLR, spaceAfter=2, spaceBefore=2))
continue
if kind == 'chapter':
fn, cap = get_drawing_for_section(text)
if fn and text not in used_drawings:
used_drawings.add(text)
try:
drw = fn()
story.append(Spacer(1, 3*mm))
story.append(GraphicsFlowable(drw))
story.append(Paragraph(cap, styles['img_caption']))
except Exception as e:
print(f" [DRAW ERR] {text}: {e}")
story.append(Spacer(1, 4*mm))
story.append(Paragraph(f"📘 {text}", styles['chapter']))
story.append(HRFlowable(width="100%", thickness=1.2,
color=HEADING_COLOR, spaceAfter=3))
continue
if kind == 'section':
fn, cap = get_drawing_for_section(text)
if fn and text not in used_drawings:
used_drawings.add(text)
try:
drw = fn()
story.append(Spacer(1, 3*mm))
story.append(GraphicsFlowable(drw))
story.append(Paragraph(cap, styles['img_caption']))
except Exception as e:
print(f" [DRAW ERR] {text}: {e}")
story.append(Spacer(1, 3*mm))
story.append(Paragraph(f"🔹 {text}", styles['section']))
continue
if kind == 'arrow':
clean = text.lstrip('→').strip()
story.append(Paragraph(
f'<font color="#c0392b">▸</font> {clean}', styles['arrow']))
continue
if kind == 'bullet':
clean = re.sub(r'^[•\-·\d\.]\s*', '', text).strip()
story.append(Paragraph(
f'<font color="#27ae60">●</font> {clean}', styles['bullet']))
continue
# eponym / normal
story.append(Paragraph(f"<b>{text.strip()}</b>", styles['eponym']))
return story
# ─── Main ─────────────────────────────────────────────────────────────────────
def main():
print("Extracting text…")
pages_text = extract_pages()
print(f" {len(pages_text)} pages")
styles = make_styles()
print("Building story…")
story = build_story(pages_text, styles)
print(f" {len(story)} elements")
print("Rendering PDF…")
doc = SimpleDocTemplate(
OUTPUT_PDF,
pagesize=A4,
leftMargin=26*mm, rightMargin=14*mm,
topMargin=20*mm, bottomMargin=18*mm,
title="Eponyms – Surgery Handwritten Notes",
author="Bailey & Love",
subject="Surgery Eponyms, Signs, Classifications",
)
doc.build(story, onFirstPage=add_page_background, onLaterPages=add_page_background)
size_mb = os.path.getsize(OUTPUT_PDF) / 1024 / 1024
print(f"Done! {OUTPUT_PDF} ({size_mb:.2f} MB)")
if __name__ == '__main__':
main()
How can i proceed? I need it right now the pdf you making
Proceed now, i topped up
cat > /home/daytona/workspace/eponyms-notes/build_notes.py << 'PYEOF' #!/usr/bin/env python3 """ Convert Eponyms.pdf into a handwritten medical-notes style PDF with contextual anatomical diagrams drawn via ReportLab at topic changes. """ import pdfplumber import re import os from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.platypus import ( SimpleDocTemplate, Paragraph, Spacer, HRFlowable, PageBreak ) from reportlab.lib.styles import ParagraphStyle from reportlab.lib.enums import TA_CENTER from reportlab.graphics.shapes import ( Drawing, Ellipse, Rect, Circle, Line, String ) from reportlab.graphics.renderPDF import GraphicsFlowable INPUT_PDF = "/home/daytona/workspace/attachments/9f0a77fd-a844-45c4-9678-be9b40b85ded/Eponyms.pdf" OUTPUT_PDF = "/home/daytona/workspace/eponyms-notes/Eponyms_HandwrittenNotes.pdf" FONTS_DIR = "/home/daytona/workspace/eponyms-notes/fonts" pdfmetrics.registerFont(TTFont('Kalam', f"{FONTS_DIR}/Kalam-Regular.ttf")) pdfmetrics.registerFont(TTFont('KalamBold', f"{FONTS_DIR}/Kalam-Bold.ttf")) PAPER_BG = colors.HexColor("#FFF9F0") LINE_COLOR = colors.HexColor("#B0C4DE") HEADING_COLOR = colors.HexColor("#1a3a5c") SUBHEAD_COLOR = colors.HexColor("#2e6e9e") SEPARATOR_CLR = colors.HexColor("#aaaaaa") TITLE_COLOR = colors.HexColor("#8B0000") C = colors def make_styles(): S = {} S['cover_title'] = ParagraphStyle('cover_title', fontName='KalamBold', fontSize=38, textColor=TITLE_COLOR, leading=46, alignment=TA_CENTER, spaceAfter=6) S['cover_sub'] = ParagraphStyle('cover_sub', fontName='Kalam', fontSize=13, textColor=SUBHEAD_COLOR, leading=18, alignment=TA_CENTER, spaceAfter=4) S['chapter'] = ParagraphStyle('chapter', fontName='KalamBold', fontSize=18, textColor=HEADING_COLOR, leading=24, spaceBefore=12, spaceAfter=3) S['section'] = ParagraphStyle('section', fontName='KalamBold', fontSize=13, textColor=SUBHEAD_COLOR, leading=19, spaceBefore=9, spaceAfter=2) S['eponym'] = ParagraphStyle('eponym', fontName='KalamBold', fontSize=11, textColor=HEADING_COLOR, leading=16, spaceBefore=5, spaceAfter=1) S['arrow'] = ParagraphStyle('arrow', fontName='Kalam', fontSize=11, textColor=C.HexColor("#333333"), leading=15, spaceBefore=0, spaceAfter=1, leftIndent=12) S['bullet'] = ParagraphStyle('bullet', fontName='Kalam', fontSize=11, textColor=C.HexColor("#333333"), leading=15, spaceBefore=0, spaceAfter=1, leftIndent=20) S['normal'] = ParagraphStyle('normal', fontName='Kalam', fontSize=11, textColor=C.HexColor("#222222"), leading=16, spaceBefore=1, spaceAfter=1) S['img_caption'] = ParagraphStyle('img_caption', fontName='Kalam', fontSize=9, textColor=C.HexColor("#555555"), leading=12, spaceBefore=1, spaceAfter=5, alignment=TA_CENTER) return S # ── Diagram helpers ────────────────────────────────────────────────────────── def _base(w, h): d = Drawing(w, h) d.add(Rect(0,0,w,h, fillColor=C.HexColor("#fffdf5"), strokeColor=C.HexColor("#dddddd"), strokeWidth=0.5)) return d def _lbl(d, w, text, col="#555555"): d.add(String(w/2, 5, text, textAnchor='middle', fontName='Helvetica', fontSize=8, fillColor=C.HexColor(col))) def draw_thyroid(): d = _base(200,130) d.add(Rect(82,14,36,58, fillColor=C.HexColor("#c8e6f5"), strokeColor=C.HexColor("#4a90b0"), strokeWidth=1.2, rx=5)) d.add(Ellipse(55,78,44,52, fillColor=C.HexColor("#7ec8a0"), strokeColor=C.HexColor("#2e7d52"), strokeWidth=1.5)) d.add(Ellipse(145,78,44,52, fillColor=C.HexColor("#7ec8a0"), strokeColor=C.HexColor("#2e7d52"), strokeWidth=1.5)) d.add(Rect(76,54,48,22, fillColor=C.HexColor("#7ec8a0"), strokeColor=C.HexColor("#2e7d52"), strokeWidth=1)) for x,y in [(42,90),(68,90),(132,90),(158,90)]: d.add(Circle(x,y,5, fillColor=C.HexColor("#f5a623"), strokeColor=C.HexColor("#c07030"), strokeWidth=1)) _lbl(d,200,"Thyroid (green) & Parathyroid (orange) Glands","#2e7d52") return d def draw_breast(): d = _base(180,130) d.add(Ellipse(90,72,65,52, fillColor=C.HexColor("#f4c2a1"), strokeColor=C.HexColor("#c0784d"), strokeWidth=1.8)) d.add(Circle(90,72,10, fillColor=C.HexColor("#c0784d"), strokeColor=C.HexColor("#8b4a2b"), strokeWidth=1)) for dx,dy in [(-32,28),(32,28),(-55,0),(55,0),(-30,-28),(30,-28)]: d.add(Line(90,72,90+dx,72+dy, strokeColor=C.HexColor("#b07040"), strokeWidth=0.7)) _lbl(d,180,"Breast – Cooper's Ligaments & Nipple","#8b4a2b") return d def draw_liver(): d = _base(190,120) d.add(Ellipse(68,72,60,46, fillColor=C.HexColor("#e74c3c"), strokeColor=C.HexColor("#7b1a11"), strokeWidth=1.5)) d.add(Ellipse(138,74,42,30, fillColor=C.HexColor("#c0392b"), strokeColor=C.HexColor("#7b1a11"), strokeWidth=1.5)) d.add(Line(95,28,95,72, strokeColor=C.HexColor("#7b1a11"), strokeWidth=1)) d.add(Ellipse(80,42,13,20, fillColor=C.HexColor("#85c1e9"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1)) d.add(String(35,76,"Right Lobe", fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#fff"))) d.add(String(120,76,"Left Lobe", fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#fff"))) _lbl(d,190,"Liver – Right & Left Lobes (GB in blue)","#7b1a11") return d def draw_kidney(): d = _base(190,140) for cx,flip in [(52,-1),(138,1)]: d.add(Ellipse(cx,75,36,54, fillColor=C.HexColor("#e8a87c"), strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5)) d.add(Ellipse(cx+flip*11,75,22,36, fillColor=C.HexColor("#fffdf5"), strokeColor=None)) d.add(Ellipse(cx+flip*7,75,11,20, fillColor=C.HexColor("#f5a623"), strokeColor=C.HexColor("#b5651d"), strokeWidth=0.8)) d.add(Line(63,112,63,130, strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5)) d.add(Line(127,112,127,130, strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5)) _lbl(d,190,"Kidneys & Ureters","#b5651d") return d def draw_stomach(): d = _base(190,125) d.add(Ellipse(88,72,72,50, fillColor=C.HexColor("#f7dc6f"), strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1.5)) d.add(Ellipse(60,52,30,28, fillColor=C.HexColor("#f7dc6f"), strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1)) d.add(Rect(138,60,18,22, fillColor=C.HexColor("#f0ca2f"), strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1, rx=6)) d.add(String(60,52,"Fundus", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#333"))) d.add(String(155,70,"Pylorus", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#333"))) _lbl(d,190,"Stomach Anatomy","#9a7d0a") return d def draw_pancreas(): d = _base(220,95) d.add(Rect(0,0,220,95, fillColor=C.HexColor("#fffdf5"), strokeColor=C.HexColor("#dddddd"), strokeWidth=0.5)) d.add(Ellipse(46,52,42,36, fillColor=C.HexColor("#f0b27a"), strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(Rect(82,36,72,32, fillColor=C.HexColor("#f0b27a"), strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1)) d.add(Ellipse(178,56,28,24, fillColor=C.HexColor("#f0b27a"), strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(Line(18,52,200,52, strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(String(46,52,"Head", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#fff"))) d.add(String(118,52,"Body", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#fff"))) d.add(String(178,52,"Tail", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#fff"))) d.add(String(110,5,"Pancreas – Head, Body, Tail", textAnchor='middle', fontName='Helvetica', fontSize=8, fillColor=C.HexColor("#ca6f1e"))) return d def draw_gallbladder(): d = _base(180,140) d.add(Ellipse(72,112,56,24, fillColor=C.HexColor("#e74c3c"), strokeColor=C.HexColor("#7b1a11"), strokeWidth=1)) d.add(Ellipse(84,78,28,42, fillColor=C.HexColor("#a9cce3"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5)) d.add(Ellipse(84,42,18,18, fillColor=C.HexColor("#a9cce3"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1)) d.add(Line(92,30,110,14, strokeColor=C.HexColor("#1a5276"), strokeWidth=2)) d.add(String(84,112,"Liver", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#fff"))) d.add(String(110,20,"CBD", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#1a5276"))) _lbl(d,180,"Gallbladder & Biliary System","#1a5276") return d def draw_colon(): d = _base(210,150) d.add(Rect(0,0,210,150, fillColor=C.HexColor("#fffdf5"), strokeColor=C.HexColor("#dddddd"), strokeWidth=0.5)) col=C.HexColor("#8e6b3e"); sw=7 d.add(Line(42,18,42,118, strokeColor=col, strokeWidth=sw)) d.add(Line(42,118,168,118, strokeColor=col, strokeWidth=sw)) d.add(Line(168,118,168,18, strokeColor=col, strokeWidth=sw)) d.add(Ellipse(130,30,34,20, fillColor=None, strokeColor=col, strokeWidth=sw)) d.add(Ellipse(42,28,16,16, fillColor=C.HexColor("#f0b27a"), strokeColor=col, strokeWidth=2)) d.add(Ellipse(32,14,6,14, fillColor=C.HexColor("#e74c3c"), strokeColor=col, strokeWidth=1.5)) d.add(String(20,120,"Ascending", fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#555"))) d.add(String(100,128,"Transverse", fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#555"), textAnchor='middle')) d.add(String(185,120,"Descending", fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#555"), textAnchor='end')) d.add(String(210/2,5,"Large Intestine / Colon", textAnchor='middle', fontName='Helvetica', fontSize=8, fillColor=C.HexColor("#8e6b3e"))) return d def draw_lung(): d = _base(190,140) d.add(Ellipse(52,76,36,58, fillColor=C.HexColor("#85c1e9"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5)) d.add(Line(46,48,58,48, strokeColor=C.HexColor("#1a5276"), strokeWidth=1)) d.add(Ellipse(138,76,40,58, fillColor=C.HexColor("#85c1e9"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5)) d.add(Line(128,36,148,36, strokeColor=C.HexColor("#1a5276"), strokeWidth=1)) d.add(Line(128,52,148,52, strokeColor=C.HexColor("#1a5276"), strokeWidth=1)) d.add(Rect(80,16,30,52, fillColor=C.HexColor("#c8e6f5"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1.2, rx=5)) d.add(Line(80,40,52,28, strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5)) d.add(Line(110,40,138,28, strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5)) d.add(String(52,76,"L", textAnchor='middle', fontName='Helvetica-Bold', fontSize=9, fillColor=C.HexColor("#fff"))) d.add(String(138,76,"R", textAnchor='middle', fontName='Helvetica-Bold', fontSize=9, fillColor=C.HexColor("#fff"))) _lbl(d,190,"Lungs & Tracheobronchial Tree","#1a5276") return d def draw_spleen(): d = _base(180,120) d.add(Ellipse(90,65,68,50, fillColor=C.HexColor("#9b59b6"), strokeColor=C.HexColor("#5b2c6f"), strokeWidth=1.5)) d.add(Line(34,78,46,60, strokeColor=C.HexColor("#5b2c6f"), strokeWidth=2)) d.add(Ellipse(118,65,8,20, fillColor=C.HexColor("#fffdf5"), strokeColor=None)) _lbl(d,180,"Spleen","#5b2c6f") return d def draw_appendix(): d = _base(180,145) d.add(Ellipse(82,102,44,40, fillColor=C.HexColor("#f0b27a"), strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(Rect(16,90,48,18, fillColor=C.HexColor("#f7dc6f"), strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1, rx=6)) d.add(Ellipse(88,55,11,50, fillColor=C.HexColor("#e74c3c"), strokeColor=C.HexColor("#922b21"), strokeWidth=1.5)) d.add(String(112,58,"McBurney's Pt", fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#555"))) d.add(Line(112,56,94,52, strokeColor=C.HexColor("#e74c3c"), strokeWidth=0.8)) _lbl(d,180,"Appendix & Ileocecal Region","#922b21") return d def draw_skin(): d = _base(195,125) d.add(Rect(0,0,195,125, fillColor=C.HexColor("#fffdf5"), strokeColor=C.HexColor("#dddddd"), strokeWidth=0.5)) for y,bh,fc,sc,lbl in [(90,16,C.HexColor("#f4d03f"),C.HexColor("#9a7d0a"),"Epidermis"), (65,26,C.HexColor("#f0b27a"),C.HexColor("#ca6f1e"),"Dermis"), (30,36,C.HexColor("#fad7a0"),C.HexColor("#b7770d"),"Subcutaneous fat")]: d.add(Rect(12,y,171,bh, fillColor=fc, strokeColor=sc, strokeWidth=1)) d.add(String(22,y+bh/2-4,lbl, fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#333"))) d.add(String(97,5,"Skin Layers (Wound / Burns)", textAnchor='middle', fontName='Helvetica', fontSize=8, fillColor=C.HexColor("#9a7d0a"))) return d def draw_vascular(): d = _base(180,150) col=C.HexColor("#e74c3c") d.add(Rect(78,18,24,122, fillColor=col, strokeColor=C.HexColor("#922b21"), strokeWidth=1.5, rx=6)) for yv,xl,xr,lbl in [(120,42,138,"Renals"),(98,36,144,"SMA"),(76,32,148,"Celiac"),(54,38,142,"Iliacs")]: d.add(Line(78,yv,xl,yv, strokeColor=col, strokeWidth=2)) d.add(Line(102,yv,xr,yv, strokeColor=col, strokeWidth=2)) d.add(String(20,yv-4,lbl, fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#555"))) d.add(Line(90,28,58,14, strokeColor=col, strokeWidth=2.5)) d.add(Line(90,28,122,14, strokeColor=col, strokeWidth=2.5)) _lbl(d,180,"Aorta & Major Branches","#922b21") return d def draw_hernia(): d = _base(180,135) d.add(Rect(12,66,156,26, fillColor=C.HexColor("#85c1e9"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5)) d.add(Rect(72,66,36,26, fillColor=C.HexColor("#fffdf5"), strokeColor=None)) d.add(Ellipse(90,50,32,24, fillColor=C.HexColor("#f0b27a"), strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(Rect(12,90,156,9, fillColor=C.HexColor("#f7dc6f"), strokeColor=C.HexColor("#9a7d0a"), strokeWidth=0.8)) d.add(String(125,76,"Defect", fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#555"))) d.add(String(90,50,"Hernia sac", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#555"))) _lbl(d,180,"Hernia – Abdominal Wall Defect","#ca6f1e") return d def draw_anal(): d = _base(180,150) d.add(Rect(54,72,72,68, fillColor=C.HexColor("#f0b27a"), strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5, rx=10)) d.add(Rect(64,28,52,48, fillColor=C.HexColor("#e8a87c"), strokeColor=C.HexColor("#ca6f1e"), strokeWidth=1.5)) d.add(Ellipse(90,32,40,12, fillColor=None, strokeColor=C.HexColor("#922b21"), strokeWidth=2)) d.add(Line(64,54,116,54, strokeColor=C.HexColor("#1a5276"), strokeWidth=1.2)) d.add(String(120,50,"Dentate line", fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#1a5276"))) d.add(String(90,100,"Rectum", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#fff"))) d.add(String(90,50,"Anal Canal", textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#333"))) _lbl(d,180,"Rectum & Anal Canal","#ca6f1e") return d def draw_neck(): d = _base(180,150) d.add(Rect(54,18,72,122, fillColor=C.HexColor("#fde9d9"), strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5, rx=20)) d.add(Rect(76,28,28,85, fillColor=C.HexColor("#c8e6f5"), strokeColor=C.HexColor("#4a90b0"), strokeWidth=1.2, rx=5)) d.add(Ellipse(67,88,18,26, fillColor=C.HexColor("#7ec8a0"), strokeColor=C.HexColor("#2e7d52"), strokeWidth=1)) d.add(Ellipse(113,88,18,26, fillColor=C.HexColor("#7ec8a0"), strokeColor=C.HexColor("#2e7d52"), strokeWidth=1)) d.add(Line(57,118,57,138, strokeColor=C.HexColor("#e74c3c"), strokeWidth=3)) d.add(Line(123,118,123,138, strokeColor=C.HexColor("#e74c3c"), strokeWidth=3)) d.add(String(45,130,"Carotid", fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#e74c3c"), textAnchor='end')) _lbl(d,180,"Neck – Trachea, Thyroid, Carotids","#1a5276") return d def draw_lymph(): d = _base(140,150) xs=[70,70,70,52,88,52,88]; ys=[128,106,84,62,62,40,40] for i in range(len(xs)-1): d.add(Line(xs[i],ys[i],xs[i+1],ys[i+1], strokeColor=C.HexColor("#5dade2"), strokeWidth=1)) for x,y in zip(xs,ys): d.add(Ellipse(x,y,14,10, fillColor=C.HexColor("#a9cce3"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1.2)) _lbl(d,140,"Lymph Node Chain","#1a5276") return d def draw_bone(): d = _base(140,160) d.add(Ellipse(70,143,38,16, fillColor=C.HexColor("#f5f5dc"), strokeColor=C.HexColor("#8b7355"), strokeWidth=1.5)) d.add(Rect(54,30,32,114, fillColor=C.HexColor("#f5f5dc"), strokeColor=C.HexColor("#8b7355"), strokeWidth=1.5)) d.add(Rect(60,40,20,94, fillColor=C.HexColor("#ffe4b5"), strokeColor=None)) d.add(Rect(51,30,38,114, fillColor=None, strokeColor=C.HexColor("#aaa"), strokeWidth=0.7)) d.add(Ellipse(70,17,38,16, fillColor=C.HexColor("#f5f5dc"), strokeColor=C.HexColor("#8b7355"), strokeWidth=1.5)) _lbl(d,140,"Long Bone – Epiphysis / Diaphysis","#8b7355") return d def draw_brain(): d = _base(195,135) d.add(Ellipse(97,78,85,60, fillColor=C.HexColor("#ffcba4"), strokeColor=C.HexColor("#b5651d"), strokeWidth=1.5)) d.add(Line(97,134,97,22, strokeColor=C.HexColor("#b5651d"), strokeWidth=1)) d.add(Line(30,95,164,95, strokeColor=C.HexColor("#b5651d"), strokeWidth=0.8)) d.add(Line(62,134,42,84, strokeColor=C.HexColor("#b5651d"), strokeWidth=0.8)) d.add(Ellipse(97,28,52,22, fillColor=C.HexColor("#f4d03f"), strokeColor=C.HexColor("#9a7d0a"), strokeWidth=1)) for x,y,lbl in [(60,112,"Frontal"),(130,112,"Parietal"),(60,72,"Temporal"),(130,72,"Occipital"),(97,28,"Cerebellum")]: d.add(String(x,y,lbl, textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#333"))) _lbl(d,195,"Brain – Lobes & Cerebellum","#b5651d") return d def draw_salivary(): d = _base(195,135) for cx,lbl in [(44,"Parotid"),(151,"Parotid")]: d.add(Ellipse(cx,78,38,50, fillColor=C.HexColor("#a9cce3"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1.5)) d.add(String(cx,24,lbl, textAnchor='middle', fontName='Helvetica', fontSize=7, fillColor=C.HexColor("#1a5276"))) for cx,lbl in [(66,"Submand."),(129,"Submand.")]: d.add(Ellipse(cx,102,24,18, fillColor=C.HexColor("#85c1e9"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1)) d.add(String(cx,84,lbl, textAnchor='middle', fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#555"))) d.add(Ellipse(97,112,20,12, fillColor=C.HexColor("#d6eaf8"), strokeColor=C.HexColor("#1a5276"), strokeWidth=1)) d.add(String(97,102,"Sublingual", textAnchor='middle', fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#555"))) _lbl(d,195,"Salivary Glands – Parotid, Submandibular, Sublingual","#1a5276") return d def draw_esophagus(): d = _base(140,170) d.add(Rect(50,18,40,142, fillColor=C.HexColor("#f4c2a1"), strokeColor=C.HexColor("#c0784d"), strokeWidth=1.5, rx=12)) for yc,lbl in [(148,"Cricopharyngeus"),(100,"Aortic arch"),(26,"Diaphragm")]: d.add(Rect(56,yc-5,28,10, fillColor=C.HexColor("#f5f0e8"), strokeColor=C.HexColor("#c0784d"), strokeWidth=0.8)) d.add(String(88,yc-2,lbl, fontName='Helvetica', fontSize=6, fillColor=C.HexColor("#555"))) _lbl(d,140,"Oesophagus (3 Constrictions)","#c0784d") return d SECTION_DRAWINGS = { "THYROID":(draw_thyroid,"Thyroid & Parathyroid"), "PARATHYROID":(draw_thyroid,"Parathyroid Glands"), "BREAST":(draw_breast,"Breast Anatomy"), "LIVER":(draw_liver,"Liver"), "HEPAT":(draw_liver,"Hepatic Anatomy"), "PANCREA":(draw_pancreas,"Pancreas"), "APPENDIX":(draw_appendix,"Appendix"), "APPEND":(draw_appendix,"Appendix"), "COLON":(draw_colon,"Large Intestine"), "COLORECT":(draw_colon,"Colorectal"), "RECTUM":(draw_anal,"Rectum & Anal Canal"), "ANAL":(draw_anal,"Anal Canal"), "ANORECTAL":(draw_anal,"Anorectal"), "INGUINAL":(draw_hernia,"Inguinal / Hernia"), "HERNIA":(draw_hernia,"Hernia"), "STOMACH":(draw_stomach,"Stomach"), "GASTRIC":(draw_stomach,"Gastric"), "OESOPH":(draw_esophagus,"Oesophagus"), "ESOPH":(draw_esophagus,"Oesophagus"), "KIDNEY":(draw_kidney,"Kidneys"), "RENAL":(draw_kidney,"Renal"), "ADRENAL":(draw_kidney,"Adrenal & Kidney"), "SPLEEN":(draw_spleen,"Spleen"), "SPLENIC":(draw_spleen,"Spleen"), "GALLBLADDER":(draw_gallbladder,"Gallbladder"), "BILIARY":(draw_gallbladder,"Biliary System"), "BILE":(draw_gallbladder,"Bile Ducts"), "VASCULAR":(draw_vascular,"Vascular System"), "AORTA":(draw_vascular,"Aorta"), "CAROTID":(draw_neck,"Neck / Carotid"), "NECK":(draw_neck,"Neck Anatomy"), "PAROTID":(draw_salivary,"Salivary Glands"), "SALIVARY":(draw_salivary,"Salivary Glands"), "LUNG":(draw_lung,"Lungs"), "THORAC":(draw_lung,"Thorax"), "CHEST":(draw_lung,"Chest"), "WOUND":(draw_skin,"Wound / Skin"), "SKIN":(draw_skin,"Skin Layers"), "BURNS":(draw_skin,"Burns"), "BURN":(draw_skin,"Burns"), "LYMPH":(draw_lymph,"Lymph Nodes"), "BRAIN":(draw_brain,"Brain Lobes"), "SKULL":(draw_brain,"Skull / Brain"), "HEAD":(draw_brain,"Head / Brain"), "BONE":(draw_bone,"Bone Structure"), "FRACTURE":(draw_bone,"Fracture / Bone"), "ORTHOPAED":(draw_bone,"Orthopaedics"), "ORTHOPED":(draw_bone,"Orthopaedics"), "UROLOGY":(draw_kidney,"Urological Anatomy"), "UROLOG":(draw_kidney,"Urology"), "BLADDER":(draw_kidney,"Urinary Bladder"), "PROSTATE":(draw_kidney,"Prostate"), "TESTIS":(draw_kidney,"Testis"), "TESTICULAR":(draw_kidney,"Testicular"), "SCROTAL":(draw_kidney,"Scrotal"), "VARICOSE":(draw_vascular,"Venous System"), "VEIN":(draw_vascular,"Veins"), "PERITON":(draw_stomach,"Peritoneal Cavity"), "DUODENUM":(draw_stomach,"Duodenum"), "HIP":(draw_bone,"Hip Joint"), "SHOULDER":(draw_bone,"Shoulder"), "TRAUMA":(draw_bone,"Trauma"), "LAPAROSCOP":(draw_liver,"Laparoscopy"), "ENDOSCOP":(draw_esophagus,"Endoscopy"), "INFECTION":(draw_skin,"Infection"), "SEPSIS":(draw_vascular,"Sepsis"), "SHOCK":(draw_vascular,"Shock"), "ONCOLOGY":(draw_lymph,"Surgical Oncology"), "INCISION":(draw_skin,"Surgical Incision"), } def get_drawing_for_section(text): upper = text.upper() for kw,(fn,cap) in SECTION_DRAWINGS.items(): if kw in upper: return fn, cap return None, None def extract_pages(): pages = [] with pdfplumber.open(INPUT_PDF) as pdf: for page in pdf.pages: pages.append(page.extract_text() or "") return pages def classify_line(line): s = line.strip() if not s: return ('blank',s) if s in ('⸻','---') or (len(s)>2 and len(set(s).difference({'─','-','—','⸻','–',' '}))==0): return ('separator',s) if re.match(r'^\d+\.\s+[A-Z][A-Z\s/]+$',s) and len(s)<80: return ('chapter',s) if re.match(r'^[A-Z]\.\s+[A-Z]',s) and len(s)<80: return ('section',s) if s.startswith('→'): return ('arrow',s) if s.startswith('•') or re.match(r'^[-·]\s',s): return ('bullet',s) if re.match(r'^\d+\.\s+[a-z]',s) or re.match(r'^\d+\.\s+[A-Z][a-z]',s): return ('bullet',s) return ('eponym',s) def add_page_background(canvas, doc): canvas.saveState() W,H = A4 canvas.setFillColor(PAPER_BG); canvas.rect(0,0,W,H,fill=1,stroke=0) canvas.setStrokeColor(LINE_COLOR); canvas.setLineWidth(0.3) y=H-18*mm while y>14*mm: canvas.line(20*mm,y,W-12*mm,y); y-=7.2*mm canvas.setStrokeColor(colors.HexColor("#e8a0a0")); canvas.setLineWidth(0.9) canvas.line(24*mm,H-10*mm,24*mm,12*mm) canvas.setFont('Kalam',9); canvas.setFillColor(colors.HexColor("#888888")) canvas.drawCentredString(W/2,8*mm,f"\u2015 {doc.page} \u2015") canvas.restoreState() def build_story(pages_text, styles): story = [] used = set() story.append(Spacer(1,28*mm)) story.append(Paragraph("✦ Eponyms ✦", styles['cover_title'])) story.append(Spacer(1,3*mm)) story.append(Paragraph("Surgery Quick Notes", styles['cover_sub'])) story.append(Paragraph("Bailey & Love · MBBS + INI-CET High-Yield", styles['cover_sub'])) story.append(Spacer(1,5*mm)) story.append(HRFlowable(width="65%",thickness=1.5,color=SUBHEAD_COLOR,spaceAfter=5)) story.append(Paragraph("Named Signs · Classifications · Scoring · Imaging", styles['cover_sub'])) story.append(Spacer(1,55*mm)) story.append(PageBreak()) for page_text in pages_text: for raw in page_text.split('\n'): kind,text = classify_line(raw) if kind=='blank': story.append(Spacer(1,1.8*mm)); continue if kind=='separator': story.append(HRFlowable(width="88%",thickness=0.5,color=SEPARATOR_CLR,spaceAfter=2,spaceBefore=2)); continue if kind in ('chapter','section'): fn,cap = get_drawing_for_section(text) if fn and text not in used: used.add(text) try: drw = fn() story.append(Spacer(1,3*mm)) story.append(GraphicsFlowable(drw)) story.append(Paragraph(cap, styles['img_caption'])) except Exception as e: print(f" [DRAW ERR] {text}: {e}") if kind=='chapter': story.append(Spacer(1,4*mm)) story.append(Paragraph(f"📘 {text}", styles['chapter'])) story.append(HRFlowable(width="100%",thickness=1.2,color=HEADING_COLOR,spaceAfter=3)) else: story.append(Spacer(1,3*mm)) story.append(Paragraph(f"🔹 {text}", styles['section'])) continue if kind=='arrow': clean=text.lstrip('→').strip() story.append(Paragraph(f'<font color="#c0392b">▸</font> {clean}', styles['arrow'])); continue if kind=='bullet': clean=re.sub(r'^[•\-·\d\.]\s*','',text).strip() story.append(Paragraph(f'<font color="#27ae60">●</font> {clean}', styles['bullet'])); continue story.append(Paragraph(f"<b>{text.strip()}</b>", styles['eponym'])) return story def main(): print("Extracting text...") pages_text = extract_pages() print(f" {len(pages_text)} pages") styles = make_styles() print("Building story...") story = build_story(pages_text, styles) print(f" {len(story)} elements") print("Rendering PDF...") doc = SimpleDocTemplate( OUTPUT_PDF, pagesize=A4, leftMargin=28*mm, rightMargin=14*mm, topMargin=20*mm, bottomMargin=18*mm, title="Eponyms – Surgery Handwritten Notes", author="Bailey & Love", subject="Surgery Eponyms, Signs, Classifications", ) doc.build(story, onFirstPage=add_page_background, onLaterPages=add_page_background) size_mb = os.path.getsize(OUTPUT_PDF)/1024/1024 print(f"Done! {OUTPUT_PDF} ({size_mb:.2f} MB)") if __name__=='__main__': main() PYEOF echo "Script written OK"
cd /home/daytona/workspace/eponyms-notes && python3 build_notes.py 2>&1
Eponyms HandwrittenNotes
PDF Document · PDF