Age estimation by dray in forensic medicine
Dray method age estimation forensic medicine teeth dental forensic odontology
"Dray" OR "Drew" age estimation teeth forensic medicine India ground sections
Dray method age estimation forensic odontology teeth six criteria India forensic medicine textbook
Age estimation by xray in forensic medicine
age estimation by xray radiological methods forensic medicine epiphyseal fusion ossification India
| Age Group | Recommended Region for X-Ray |
|---|---|
| 6-12 years | Elbow and wrist |
| 13-16 years | Elbow and pelvis |
| Any age | Standard right-sided X-rays in AP view (elbow: AP + lateral) |
Wrist and hand are the most studied regions by researchers worldwide.
| Age | Ossification Centre Appears | Epiphyseal Fusion/Union |
|---|---|---|
| 5th year | Head of radius, trapezoid, scaphoid | Greater tubercle fuses with head of humerus |
| 6th year | Lower end of ulna, trapezium | Rami of pubis and ischium unite |
| 6-7th year | Medial epicondyle of humerus | - |
| 9th year | Olecranon | - |
| 9-11th year | Trochlea of humerus | - |
| 10-11th year | Pisiform | - |
| 11th year | Lateral epicondyle of humerus | - |
| 12-14th year | Lesser trochanter of femur | - |
| 13th year | Triradiate cartilage of acetabulum (separate centres) | - |
| 14th year | Crest of ilium; head and tubercles of ribs | Medial epicondyle of humerus; lateral epicondyle with trochlea; patella complete |
| 15th year | Acromion | Coracoid with scapula; triradiate cartilage of acetabulum |
| 16th year | Ischial tuberosity | Lower end of humerus; olecranon to ulna; upper end of radius; metacarpals; proximal phalanges |
| 17-18th year | - | Head of femur; lesser and greater trochanter of femur; acromion; lower end of ulna |
| 18-19th year | Inner end of clavicle | Lower end of femur; upper end of tibia and fibula; head of humerus; lower end of radius |
| 20-21st year | - | Iliac crest; inner end of clavicle; ischial tuberosity; head of ribs |
Medico-legally critical: Lower end of femur and upper end of tibia fuse at 18-19 years, making them key markers for the 18-year threshold.
Liver cirrhosis Russel viper Summons Brain knife Castor Rifle bullet Subdural hemorrhage [Washer women's hand Hammer Give points for spotters in fmt
forensic medicine spotter "liver cirrhosis" "castor" "brain knife" "washer woman hand" "hammer" FMT spotter points
Russell viper venom forensic medicine spotter features bite marks hemotoxic coagulation forensic
"brain knife" forensic medicine spotter pathology knife wound brain
| Feature | Subdural Hematoma |
|---|---|
| Incidence | 5% of all head injuries; 50% of fatal head injuries |
| Cause | Mostly trauma; occasionally non-traumatic (massive leakage) |
| External signs | Often NO external manifestation |
| Shape | Crescent-shaped, unilateral or bilateral, diffuse |
| Brain effect | Compressed but less ironed out (dura external to blood) |
| Clinical course | Less well defined than EDH; no classic lucid interval |
| At autopsy | Washed away by thin stream of water |
| Injury Type | Description |
|---|---|
| Contusion | Bruising corresponding to shape of hammer face |
| Laceration | Split skin wound with crushed, bruised edges, tissue bridging |
| Depressed skull fracture | Patterned fracture matching hammer face dimensions |
| Extradural hematoma | From skull fracture causing meningeal artery damage |
| Brain injury | Contusion, laceration, diffuse axonal injury |
Create a comprehensive PDF spotter guide with images
mkdir -p /home/daytona/workspace/fmt-spotter-guide && ls /home/daytona/workspace/fmt-spotter-guide/
Create a comprehensive PDF spotter guide with images
~/fmt-spotter-guide/build_pdf.py
#!/usr/bin/env python3
"""
FMT Spotter Guide PDF Builder
Generates a comprehensive Forensic Medicine & Toxicology spotter guide with images.
"""
import os
import urllib.request
import io
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
Image, PageBreak, HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import BalancedColumns
from reportlab.pdfgen import canvas
OUTPUT_PATH = "/home/daytona/workspace/fmt-spotter-guide/FMT_Spotter_Guide.pdf"
IMG_DIR = "/home/daytona/workspace/fmt-spotter-guide/imgs/"
os.makedirs(IMG_DIR, exist_ok=True)
# ── colour palette ──────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1A3A5C")
MED_BLUE = colors.HexColor("#2563A8")
LIGHT_BLUE = colors.HexColor("#D6E4F7")
ACCENT = colors.HexColor("#E8472A")
GOLD = colors.HexColor("#F0A500")
LIGHT_GREY = colors.HexColor("#F4F6F9")
DARK_GREY = colors.HexColor("#444444")
WHITE = colors.white
# ── image download helper ────────────────────────────────────────────────────
def dl(url, fname):
dest = os.path.join(IMG_DIR, fname)
if not os.path.exists(dest):
try:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=15) as r, open(dest, "wb") as f:
f.write(r.read())
except Exception as e:
print(f" [WARN] Could not download {fname}: {e}")
return None
return dest if os.path.exists(dest) else None
# ── image catalogue ──────────────────────────────────────────────────────────
IMAGES = {
"russell_viper": dl("https://cdn.orris.care/cdss_images/323170d93c1d4a33a625fbd68e6af6139fd7c87b3f085597a88dc80ee6aaa4a1.png", "russell_viper.png"),
"washer_hand": dl("https://cdn.orris.care/cdss_images/3e62fbcbde3a1fb91622d787434934d1bd80804722610cb31f7c2b70cb517bd4.png", "washer_hand.png"),
"washer_foot": dl("https://cdn.orris.care/cdss_images/1b39daa1c1b7669a790da7864bd4c0f2679527875ed1b97ca04bfde930395580.png", "washer_foot.png"),
"subdural_base": dl("https://cdn.orris.care/cdss_images/9fcc1fda214392592ff24c9fcb6ff3a1ee1b1afa3654ee2e61e1b500d25136e9.png", "subdural_base.png"),
"subdural_oval": dl("https://cdn.orris.care/cdss_images/4fb1ebfffabc83f8bd006098d8ca3d5058fd2dac212372061b9894669092b272.png", "subdural_oval.png"),
"subarachnoid": dl("https://cdn.orris.care/cdss_images/26b97a1ed3d7f51d0e0eca00251c3df51dbf0838e94422caa20b925b0e4598eb.png", "subarachnoid.png"),
"castor_plant": dl("https://cdn.orris.care/cdss_images/e1ff17c2aa2b7d074233cb4d778cba9c3a9e23420a3473d662d5b23604ac6d04.png", "castor_plant.png"),
"castor_seeds": dl("https://cdn.orris.care/cdss_images/3d10a5781c1dfd0cc3c95a37cfc5211a29be956814075b6c82c4649083553a0b.png", "castor_seeds.png"),
"bullet_entry": dl("https://cdn.orris.care/cdss_images/1d07f6b7842c0c70a3104f2184eabab7c5374c932964a403aa92e23eed8beeae.png", "bullet_entry.png"),
"froth_drowning": dl("https://cdn.orris.care/cdss_images/7f4002d35c4b668fedbdca748d52263531044ff14e7ffbbead32c77d1c79e2df.png", "froth_drowning.png"),
"cutis_anserina": dl("https://cdn.orris.care/cdss_images/68ebc9533de7d135f5ace8ab67c4c35f5195bded6d5d83cc7701d0887242160d.png", "cutis_anserina.png"),
"rifled_bore": dl("https://cdn.orris.care/cdss_images/ff9e55c5823b5145b7e0f5f177e8a22d0e6bc4c33b830933f816e262e0ccaa1c.png", "rifled_bore.png"),
}
# ── styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent="Normal", **kwargs):
return ParagraphStyle(name, parent=styles[parent], **kwargs)
cover_title = make_style("CoverTitle", fontSize=34, textColor=WHITE, leading=42, alignment=TA_CENTER, fontName="Helvetica-Bold")
cover_sub = make_style("CoverSub", fontSize=16, textColor=LIGHT_BLUE, leading=22, alignment=TA_CENTER)
cover_tag = make_style("CoverTag", fontSize=11, textColor=GOLD, leading=16, alignment=TA_CENTER, fontName="Helvetica-BoldOblique")
sec_title = make_style("SecTitle", fontSize=18, textColor=WHITE, leading=24, fontName="Helvetica-Bold", alignment=TA_LEFT, spaceAfter=0)
sub_head = make_style("SubHead", fontSize=12, textColor=DARK_BLUE, leading=16, fontName="Helvetica-Bold", spaceAfter=4, spaceBefore=8)
body = make_style("Body", fontSize=9.5, textColor=DARK_GREY, leading=15, spaceAfter=3, alignment=TA_JUSTIFY)
bullet_style = make_style("BulletStyle", fontSize=9.5, textColor=DARK_GREY, leading=14, leftIndent=12, firstLineIndent=-10, spaceAfter=2, bulletText="•")
img_caption = make_style("ImgCaption", fontSize=8, textColor=colors.grey, leading=10, alignment=TA_CENTER, fontName="Helvetica-Oblique")
toc_title = make_style("TocTitle", fontSize=22, textColor=DARK_BLUE, leading=28, fontName="Helvetica-Bold", alignment=TA_CENTER, spaceAfter=12)
toc_entry = make_style("TocEntry", fontSize=11, textColor=DARK_GREY, leading=18)
# ── helpers ───────────────────────────────────────────────────────────────────
def section_banner(num, title, emoji=""):
banner_data = [[Paragraph(f"{num}. {emoji} {title}", sec_title)]]
t = Table(banner_data, colWidths=[17*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING",(0,0),(-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 14),
("ROUNDEDCORNERS", [6]),
]))
return t
def info_table(rows, col1=5.5*cm, col2=11*cm):
"""Two-column key-value table."""
data = []
for k, v in rows:
data.append([
Paragraph(f"<b>{k}</b>", make_style(f"k{k}", fontSize=9, textColor=DARK_BLUE, leading=13)),
Paragraph(v, body)
])
t = Table(data, colWidths=[col1, col2])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), LIGHT_BLUE),
("BACKGROUND", (1,0), (1,-1), WHITE),
("ROWBACKGROUNDS",(1,0),(1,-1), [WHITE, LIGHT_GREY]),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 7),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#CADCEE")),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
def bullet_list(items):
return [Paragraph(f"• {i}", bullet_style) for i in items]
def img_block(key, caption, width=7*cm):
path = IMAGES.get(key)
if path:
try:
im = Image(path, width=width, height=width*0.75)
cap = Paragraph(caption, img_caption)
data = [[im], [cap]]
t = Table(data, colWidths=[width+1*cm])
t.setStyle(TableStyle([
("ALIGN", (0,0), (-1,-1), "CENTER"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING",(0,0), (-1,-1), 3),
("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#CADCEE")),
("BACKGROUND",(0,0),(-1,-1), LIGHT_GREY),
]))
return t
except Exception as e:
print(f" [WARN] Could not embed image {key}: {e}")
return Spacer(1, 1)
def two_images(k1, c1, k2, c2, w=7*cm):
i1 = img_block(k1, c1, w)
i2 = img_block(k2, c2, w)
t = Table([[i1, i2]], colWidths=[8.5*cm, 8.5*cm])
t.setStyle(TableStyle([("ALIGN",(0,0),(-1,-1),"CENTER"),("VALIGN",(0,0),(-1,-1),"TOP")]))
return t
def comparison_table(headers, rows):
data = [headers] + rows
col_w = [17*cm / len(headers)] * len(headers)
t = Table(data, colWidths=col_w)
style = [
("BACKGROUND", (0,0), (-1,0), MED_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("LEADING", (0,0), (-1,-1), 12),
("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, LIGHT_GREY]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#CADCEE")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ALIGN", (0,0), (-1,0), "CENTER"),
]
t.setStyle(TableStyle(style))
return t
def sp(h=6):
return Spacer(1, h)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#CADCEE"), spaceAfter=4, spaceBefore=4)
# ── page layout ───────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = A4
MARGIN = 2*cm
def on_page(canvas_obj, doc):
"""Header/footer on every page except cover."""
pg = doc.page
if pg == 1:
return
canvas_obj.saveState()
# header
canvas_obj.setFillColor(DARK_BLUE)
canvas_obj.rect(0, PAGE_H - 1.2*cm, PAGE_W, 1.2*cm, fill=1, stroke=0)
canvas_obj.setFillColor(WHITE)
canvas_obj.setFont("Helvetica-Bold", 8)
canvas_obj.drawString(MARGIN, PAGE_H - 0.8*cm, "FMT SPOTTER GUIDE")
canvas_obj.setFont("Helvetica", 8)
canvas_obj.drawRightString(PAGE_W - MARGIN, PAGE_H - 0.8*cm, "KS Narayan Reddy | Parikh's | PC Dikshit")
# footer
canvas_obj.setFillColor(DARK_BLUE)
canvas_obj.rect(0, 0, PAGE_W, 0.8*cm, fill=1, stroke=0)
canvas_obj.setFillColor(WHITE)
canvas_obj.setFont("Helvetica", 8)
canvas_obj.drawCentredString(PAGE_W/2, 0.22*cm, f"Page {pg}")
canvas_obj.restoreState()
# ── build ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=2.8*cm, bottomMargin=1.8*cm,
title="FMT Spotter Guide",
author="Orris AI",
)
story = []
# ════════════════════════════════════════════════════════════════
# COVER PAGE
# ════════════════════════════════════════════════════════════════
def cover_page(c, doc):
if doc.page == 1:
c.saveState()
# full-page gradient background
c.setFillColor(DARK_BLUE)
c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
# decorative bar top
c.setFillColor(GOLD)
c.rect(0, PAGE_H - 0.8*cm, PAGE_W, 0.8*cm, fill=1, stroke=0)
# decorative bar bottom
c.setFillColor(ACCENT)
c.rect(0, 0, PAGE_W, 0.5*cm, fill=1, stroke=0)
# side accent strip
c.setFillColor(MED_BLUE)
c.rect(0, 0, 0.6*cm, PAGE_H, fill=1, stroke=0)
c.restoreState()
story.append(Spacer(1, 3*cm))
story.append(Paragraph("FORENSIC MEDICINE", cover_title))
story.append(Paragraph("& TOXICOLOGY", cover_title))
story.append(sp(10))
story.append(Paragraph("COMPREHENSIVE SPOTTER GUIDE", cover_sub))
story.append(sp(6))
story.append(HRFlowable(width="60%", thickness=2, color=GOLD, spaceAfter=12, spaceBefore=4))
story.append(sp(4))
spotters_list = [
"1. Liver Cirrhosis", "2. Russell's Viper", "3. Summons / Subpoena",
"4. Brain Knife", "5. Castor (Ricinus communis)", "6. Rifle Bullet",
"7. Subdural Hemorrhage", "8. Washerwoman's Hand", "9. Hammer",
]
toc_cover_style = make_style("TocCover", fontSize=12, textColor=LIGHT_BLUE, leading=20, alignment=TA_CENTER)
for item in spotters_list:
story.append(Paragraph(item, toc_cover_style))
story.append(sp(20))
story.append(Paragraph("Based on KS Narayan Reddy (2026) · Parikh's · PC Dikshit", cover_tag))
story.append(Paragraph("Compiled by Orris AI Assistant", cover_tag))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 1. LIVER CIRRHOSIS
# ════════════════════════════════════════════════════════════════
story.append(section_banner("1", "LIVER CIRRHOSIS", "🫀"))
story.append(sp(8))
story.append(Paragraph("Definition", sub_head))
story.append(Paragraph(
"End-stage liver disease characterized by <b>diffuse hepatic fibrosis</b> and <b>nodular regeneration</b> of hepatocytes, "
"replacing the normal lobular architecture. It is a common finding at autopsy in forensic practice, especially in alcohol-related deaths.",
body))
story.append(sp(4))
story.append(Paragraph("Gross / Autopsy Appearance", sub_head))
story.append(info_table([
("Size & Weight", "Small, shrunken, reduced weight (normal ~1.5 kg) — liver is firm and hard"),
("Surface", "<b>Nodular 'hobnail' surface</b> — fine granular (micronodular) or irregular large nodules (macronodular)"),
("Cut Section", "Fibrous bands surrounding nodules; loss of normal lobular pattern"),
("Color", "Yellow-green (biliary/fatty); brown-red (post-hepatitic); olive-green (biliary cirrhosis)"),
("Capsule", "Thickened, wrinkled capsule"),
("Consistency", "Firm to hard (due to fibrosis)"),
("Portal HTN signs","Enlarged spleen (congestive splenomegaly), ascites, dilated portal vein"),
]))
story.append(sp(6))
story.append(Paragraph("Types (Gross Classification)", sub_head))
story.append(comparison_table(
[Paragraph("<b>Type</b>", body), Paragraph("<b>Nodule Size</b>", body), Paragraph("<b>Common Causes</b>", body)],
[
[Paragraph("Micronodular", body), Paragraph("< 3 mm", body), Paragraph("Alcoholism, metabolic liver disease, biliary obstruction", body)],
[Paragraph("Macronodular", body), Paragraph("> 3 mm", body), Paragraph("Post-hepatitis B & C, autoimmune hepatitis", body)],
[Paragraph("Mixed", body), Paragraph("Both sizes", body), Paragraph("Mixed aetiology", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Medicolegal Importance", sub_head))
for pt in [
"Chronic alcoholism → most common cause; relevant in alcohol-related deaths and intoxication cases",
"Decreased synthesis of clotting factors (fibrinogen, prothrombin, Factors V, VII, IX, X) → fatal hemorrhage",
"Portal hypertension → oesophageal varices rupture → massive haematemesis → death",
"Pre-existing disease: affects opinion on cause of death in blunt trauma / fall cases",
"Hepatic encephalopathy → impaired consciousness → accidents, falls",
"Increased susceptibility to infections → opportunistic causes of death",
"Hepatocellular carcinoma is a late complication",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 2. RUSSELL'S VIPER
# ════════════════════════════════════════════════════════════════
story.append(section_banner("2", "RUSSELL'S VIPER (Daboia russelii)", "🐍"))
story.append(sp(8))
viper_img = img_block("russell_viper", "Fig. Russell's Viper — note diamond-shaped dorsal spots and triangular head", 7.5*cm)
viper_text = [
Paragraph("Identification", sub_head),
info_table([
("Also known as", "Daboia russelii; Kander, Charn viper, Khadchitro"),
("Head", "<b>Flat, heavy, triangular</b> — broader than body; covered with small scales; <b>V-shaped white mark</b> with angle pointing forwards"),
("Body", "Short, stout with <b>three rows of diamond-shaped black/brown spots</b>; outer rows ringed with white edges; belly whitish with dark semilunar spots"),
("Pupil", "<b>Vertical (slit)</b>"),
("Fangs", "Long, <b>canalized (hollow)</b> — solenoglyphous type"),
("Tail", "Short, narrow, tapering"),
("Sub-caudal", "Plates divided into <b>two rows</b> (key ID feature)"),
("Sound", "Heard to <b>hiss loudly and continuously</b>"),
("Distribution", "Found <b>throughout India</b>"),
], col1=3.5*cm, col2=5.5*cm),
]
img_col = Table([[viper_img]], colWidths=[9*cm])
text_col = Table([[v] for v in viper_text], colWidths=[8*cm])
text_col.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),6)]))
side = Table([[text_col, img_col]], colWidths=[9*cm, 8*cm])
side.setStyle(TableStyle([("VALIGN",(0,0),(-1,-1),"TOP"),("LEFTPADDING",(0,0),(-1,-1),0)]))
story.append(side)
story.append(sp(8))
story.append(Paragraph("Venom & Toxicology", sub_head))
story.append(info_table([
("Venom type", "<b>Hemotoxic</b> (primary) + Nephrotoxic + Neurotoxic (some subspecies)"),
("Mechanism", "Activates coagulation Factors X and V → widespread fibrin formation → <b>VICC</b> (Venom-Induced Consumption Coagulopathy) → DIC"),
("Local effects", "Severe pain, swelling, hemorrhagic blisters, tissue necrosis"),
("Systemic", "Thrombocytopenia, MAHA, hemoglobinuria, bleeding from gums/IV sites/hematuria"),
("Renal", "<b>AKI</b> — hallmark of Russell's viper envenomation"),
("Neuro", "Bilateral ptosis (neurotoxic subspecies); rarely paralysis"),
("Lab findings", "Prolonged PT, APTT; low fibrinogen; thrombocytopenia; elevated creatinine"),
("Fatal dose", "40–70 mg of dried venom; bite delivers 130–250 mg"),
("Antidote", "<b>Polyvalent Anti-Snake Venom (ASV)</b> — IV, given as early as possible"),
]))
story.append(sp(6))
story.append(Paragraph("Medicolegal Importance", sub_head))
for pt in [
"Most common cause of snakebite deaths in India alongside Common Krait",
"Bite marks: Two fang punctures ~1–1.5 cm apart (canalized fangs); sometimes single or multiple marks",
"Postmortem: Extensive local necrosis, hemorrhagic areas, renal cortical pallor (AKI)",
"dRVVT (Dilute Russell's Viper Venom Time) — laboratory test for lupus anticoagulant",
"Difference from Cobra: Viper = hemotoxic, Cobra = neurotoxic; Viper fangs long & canalized, Cobra fangs short & grooved",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(sp(6))
story.append(Paragraph("Cobra vs Viper — Quick Comparison", sub_head))
story.append(comparison_table(
[Paragraph("<b>Feature</b>", body), Paragraph("<b>Cobra</b>", body), Paragraph("<b>Russell's Viper</b>", body)],
[
[Paragraph("Head", body), Paragraph("Small, round", body), Paragraph("Large, triangular", body)],
[Paragraph("Pupil", body), Paragraph("Round", body), Paragraph("Vertical slit", body)],
[Paragraph("Fangs", body), Paragraph("Short, grooved", body), Paragraph("Long, canalized", body)],
[Paragraph("Venom", body), Paragraph("Neurotoxic", body), Paragraph("Hemotoxic + Nephrotoxic", body)],
[Paragraph("Eggs", body), Paragraph("Lay eggs (oviparous)", body), Paragraph("Give birth (viviparous)", body)],
[Paragraph("Tail", body), Paragraph("Round", body), Paragraph("Short, tapering", body)],
]
))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 3. SUMMONS / SUBPOENA
# ════════════════════════════════════════════════════════════════
story.append(section_banner("3", "SUMMONS / SUBPOENA", "📜"))
story.append(sp(8))
story.append(Paragraph("Definition", sub_head))
story.append(Paragraph(
"<b>Subpoena</b> (sub = under; poena = penalty): A legal document compelling the attendance of a witness in a court of law "
"<b>under penalty</b>, on a particular day, time and place, for the purpose of giving evidence.",
body))
story.append(sp(6))
story.append(Paragraph("Key Legal Points", sub_head))
story.append(info_table([
("Legal basis", "Sections <b>63–71 BNSS</b> (formerly Sections 61–69 Cr.P.C.)"),
("Issued by", "Court — in <b>writing, in duplicate</b>, signed by presiding officer, bearing the <b>seal of the court</b>"),
("Contains", "Crime number and name of the accused person"),
("Served by", "Police officer, court officer, or public servant — one copy delivered to witness; <b>receipt signed on other copy</b> (Sec 64 BNSS)"),
("Alternative service","Registered post OR fixed on conspicuous part of residence"),
("Subpoena Duces Tecum","Requires witness to bring <b>books, documents, or things</b> under his control (Sec 94 & 195 BNSS; Sec 165 BSA)"),
("Govt. servants", "Summons sent in duplicate to <b>head of office</b> who arranges service (Sec 68 BNSS)"),
]))
story.append(sp(6))
story.append(Paragraph("Punishment for Non-Compliance", sub_head))
story.append(comparison_table(
[Paragraph("<b>Type of Court</b>", body), Paragraph("<b>Consequence</b>", body)],
[
[Paragraph("Civil case", body), Paragraph("Liable to pay damages", body)],
[Paragraph("Criminal case", body), Paragraph("Fine, imprisonment, or bailable/non-bailable warrant (Sec 206–208, BNS)", body)],
[Paragraph("Intentional non-attendance", body), Paragraph("Imprisonment up to <b>1 month</b> OR fine OR both (Sec 208, BNS)", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Important Rules for Doctors", sub_head))
for pt in [
"<b>Criminal court takes priority</b> over civil court if both summon on the same day",
"Witness may be excused only if there is a <b>valid and urgent reason</b>",
"Doctor should bring relevant records, case notes, post-mortem report, etc. if specified in summons",
"Failure to attend without justification = contempt of court",
"Witness must produce documents if asked for (Sec 94, BNSS)",
"Expert witness (doctor) gives <b>opinion evidence</b>, not factual evidence — cannot be compelled to give opinion against will",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 4. BRAIN KNIFE
# ════════════════════════════════════════════════════════════════
story.append(section_banner("4", "BRAIN KNIFE (Post-Mortem Dissection Tool)", "🔪"))
story.append(sp(8))
story.append(Paragraph("What is a Brain Knife?", sub_head))
story.append(Paragraph(
"A <b>brain knife (Virchow's brain knife)</b> is a long, broad-bladed <b>post-mortem dissection instrument</b> "
"used by forensic pathologists and anatomists to slice large solid organs — especially the brain — "
"into thin, uniform sections during autopsy.",
body))
story.append(sp(6))
story.append(Paragraph("Description & Features", sub_head))
story.append(info_table([
("Blade", "Long (<b>25–35 cm</b>), flat, broad, slightly flexible"),
("Edge", "Single sharp cutting edge"),
("Handle", "Sturdy grip handle — allows smooth, single-stroke cuts"),
("Purpose", "Slice large organs (brain, liver, kidney, spleen) in <b>one continuous stroke</b>"),
("Design goal", "Produces <b>clean, smooth sections</b> without tearing or crushing tissue"),
]))
story.append(sp(6))
story.append(Paragraph("Uses at Autopsy", sub_head))
for pt in [
"<b>Brain slicing</b>: Coronal sections at 1–1.5 cm intervals to examine internal architecture",
"Detects subdural/extradural hematoma, cerebral contusions, edema, infarcts, tumors",
"Liver slicing: Identifies nutmeg liver, cirrhosis, abscesses, metastases",
"Kidney slicing: Reveals cortical necrosis, pyelonephritis, infarcts",
"Spleen: Identifies congestion, infarcts, rupture",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(sp(6))
story.append(Paragraph("Standard Autopsy Instruments — Comparison", sub_head))
story.append(comparison_table(
[Paragraph("<b>Instrument</b>", body), Paragraph("<b>Use</b>", body), Paragraph("<b>Key Feature</b>", body)],
[
[Paragraph("Brain Knife", body), Paragraph("Slice brain/large organs", body), Paragraph("Long (25–35 cm), broad blade", body)],
[Paragraph("Enterotome", body), Paragraph("Open intestines", body), Paragraph("Blunt probe tip to prevent perforation", body)],
[Paragraph("Rib cutters (Costotome)", body), Paragraph("Cut ribs to open chest", body), Paragraph("Heavy-duty shears", body)],
[Paragraph("Virchow's skull chisel", body), Paragraph("Detach dura mater", body), Paragraph("T-shaped", body)],
[Paragraph("Stryker saw (Oscillating)", body), Paragraph("Cut skull cap", body), Paragraph("Vibrating blade", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Medicolegal Significance", sub_head))
for pt in [
"Standard mortuary instrument — its presence in autopsy kit is expected",
"Allows systematic examination of the brain to identify <b>cause and manner of death</b>",
"Critical in cases of head injury, intracranial hemorrhage, poisoning (brain edema)",
"Note: A 'brain knife' as a spotter = <b>post-mortem dissection tool</b>, NOT a murder weapon",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 5. CASTOR (Ricinus communis)
# ════════════════════════════════════════════════════════════════
story.append(section_banner("5", "CASTOR — Ricinus communis", "🌱"))
story.append(sp(8))
castor_imgs = two_images("castor_plant", "Fig. Ricinus communis plant", "castor_seeds", "Fig. Castor seeds (mottled, oval)", 6.5*cm)
story.append(castor_imgs)
story.append(sp(8))
story.append(Paragraph("Botanical Identification", sub_head))
story.append(info_table([
("Family", "Euphorbiaceae"),
("Distribution", "Grows all over India; cultivated and wild"),
("Plant", "Large herb/shrub with palmate (star-shaped), 5–11 lobed leaves"),
("Fruit", "1.2–2.5 cm long, <b>three-lobed, softly spiny capsule</b>; blue-green/rose-red (immature), brown-bristly (ripe)"),
("Seeds", "<b>Smooth, flattened-oval, mottled</b>; light/dark brown with white-yellow or grey markings; have a <b>caruncle</b> (appendage) at one end; 2 sizes — large and small (small ~1.2 cm × 0.8 cm)"),
("Oil", "Pale yellow purgative oil; <b>castor oil is NOT poisonous</b> (ricin destroyed by heat processing)"),
]))
story.append(sp(6))
story.append(Paragraph("Toxic Principles", sub_head))
story.append(info_table([
("Ricin", "<b>Toxalbumin (phytotoxin)</b> — water-soluble glycoprotein; highest concentration in seeds; most toxic part"),
("Ricinine", "Alkaloid — causes CNS excitation, convulsions"),
("RCA", "Ricinus communis agglutinin — haemagglutinin"),
("Mechanism", "Ricin B-chain enters cells; A-chain <b>inhibits protein synthesis</b> by depurinating 28S rRNA in ribosomes — stops all protein production → cell death"),
("Route", "Ingestion (chewed/broken seeds), inhalation, injection, skin contact"),
("Fatal dose", "Seed: <b>2–20 seeds</b> in adults; <b>1 seed</b> can kill a child; Ricin pure: ~1–10 μg/kg body weight"),
("Note", "Unbroken seeds swallowed whole may pass harmlessly; <b>chewed seeds are lethal</b>"),
]))
story.append(sp(6))
story.append(Paragraph("Clinical Features of Poisoning", sub_head))
for pt in [
"Burning pain in mouth and throat",
"Nausea, vomiting, profuse watery then <b>hemorrhagic diarrhea</b>",
"Severe abdominal cramps",
"Haematuria, oliguria → anuria (renal failure)",
"Hypotension, tachycardia, cardiovascular collapse",
"Convulsions, coma, death within 3–5 days",
"<b>No specific antidote</b> — treatment is supportive",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(sp(6))
story.append(Paragraph("Post-Mortem Findings", sub_head))
for pt in [
"Gastroenteritis — hemorrhagic inflammation of gastric and intestinal mucosa",
"Congestion and fatty degeneration of liver and kidneys",
"Congested spleen",
"Petechial hemorrhages in various organs",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(sp(6))
story.append(Paragraph("Medicolegal Importance", sub_head))
for pt in [
"Accidental (children eating seeds), suicidal, <b>homicidal</b> poisoning",
"<b>Bioterrorism</b>: Assassination of Georgi Markov (1978) — ricin pellet injected via umbrella tip",
"Ricin is a potential bioweapon — classified as Category B by CDC",
"Dust from castor plant: occupational hazard → asthma, allergic rhinitis, dermatitis",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 6. RIFLE BULLET
# ════════════════════════════════════════════════════════════════
story.append(section_banner("6", "RIFLE BULLET", "🔫"))
story.append(sp(8))
bullet_img_side = img_block("bullet_entry", "Fig. Entry wound from distant gunshot — small, circular, inverted margins, abrasion collar", 7.5*cm)
rifled_bore_img = img_block("rifled_bore", "Fig. Cross-section of rifled bore showing lands (L) and grooves (G)", 6*cm)
story.append(Paragraph("The Weapon — Rifled Firearms", sub_head))
story.append(info_table([
("Rifling", "Spiral grooves cut inside barrel — <b>4–7 grooves</b>; raised portions = <b>lands</b>; lowered = <b>grooves</b>"),
("Effect", "Bullet spins on longitudinal axis → <b>gyroscopic stability</b> → accuracy; prevents wobbling in flight"),
("Velocity", "Rifle: <b>~2500 ft/sec</b> (HIGH velocity); Pistol: medium (~1200 ft/sec); Revolver: low (~600–800 ft/sec)"),
("Calibre", "Land-to-land width of barrel: expressed in 1/100th inch (0.22, 0.303, 0.45) or mm (7.62, 9.0)"),
("Range", "Accuracy range: <b>600+ yards</b> (rifle); 100 yards (revolver)"),
("Twist", "One complete turn in 25–35 cm of forward motion; leaves spiral grooves on bullet"),
]))
story.append(sp(6))
r_imgs = Table([[rifled_bore_img, bullet_img_side]], colWidths=[7*cm, 9*cm])
r_imgs.setStyle(TableStyle([("VALIGN",(0,0),(-1,-1),"TOP"),("ALIGN",(0,0),(-1,-1),"CENTER")]))
story.append(r_imgs)
story.append(sp(6))
story.append(Paragraph("Entry Wound Features (Distant Shot >50 cm)", sub_head))
story.append(info_table([
("Size", "<b>Smaller than bullet diameter</b> due to skin elasticity"),
("Shape", "Circular, punched-out, <b>inverted margins</b>"),
("Abrasion collar","Present — concentric ring of abrasion from bullet spinning against skin"),
("Grease collar", "Dark ring immediately around hole from bullet's surface"),
("Blackening", "Absent (distant shot); Present in close/contact shots"),
("Tattooing", "Absent at distance; Present within 50–60 cm"),
]))
story.append(sp(6))
story.append(Paragraph("Exit Wound Features", sub_head))
for pt in [
"<b>Larger than entry wound</b> — skin pushed outward by exiting bullet",
"Margins are <b>everted, ragged, irregular, stellate / cruciate</b>",
"No abrasion collar, no grease collar, no tattooing",
"Caused by: bullet deformation, tumbling, bone fragments acting as secondary missiles",
"In high-velocity rifle wounds: exit may be very large due to <b>temporary cavity</b> effect",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(sp(6))
story.append(Paragraph("Entry vs Exit — Summary Table", sub_head))
story.append(comparison_table(
[Paragraph("<b>Feature</b>", body), Paragraph("<b>Entry Wound</b>", body), Paragraph("<b>Exit Wound</b>", body)],
[
[Paragraph("Size", body), Paragraph("Smaller (skin elasticity)", body), Paragraph("Larger", body)],
[Paragraph("Margins", body), Paragraph("Inverted, regular", body), Paragraph("Everted, ragged, stellate", body)],
[Paragraph("Abrasion collar", body), Paragraph("Present", body), Paragraph("Absent", body)],
[Paragraph("Grease collar", body), Paragraph("Present", body), Paragraph("Absent", body)],
[Paragraph("Blackening/Tattooing", body), Paragraph("At close range only", body), Paragraph("Absent", body)],
[Paragraph("Bone", body), Paragraph("Punched inward", body), Paragraph("Blasted outward ('bevelled')", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Medicolegal Importance", sub_head))
for pt in [
"Determine range of fire (contact, near, intermediate, distant) from wound characteristics",
"Establish direction of shot — critical for homicide vs suicide determination",
"Rifling marks on bullet → identify specific firearm",
"Suicide: usually contact/near range; single shot; accessible area (head, chest)",
"Homicide: may be distant shot; multiple wounds; inaccessible areas (back)",
"Accidental: circumstances of scene evidence",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 7. SUBDURAL HEMORRHAGE
# ════════════════════════════════════════════════════════════════
story.append(section_banner("7", "SUBDURAL HEMORRHAGE", "🧠"))
story.append(sp(8))
sdh_imgs = two_images("subdural_base", "Fig. Subdural hemorrhage at base of skull", "subdural_oval", "Fig. Subdural hematoma (oval, crescent-shaped)", 6.5*cm)
story.append(sdh_imgs)
story.append(sp(8))
story.append(Paragraph("Definition & Location", sub_head))
story.append(Paragraph(
"Collection of blood in the <b>subdural space</b> — between the <b>dura mater</b> and <b>arachnoid mater</b>. "
"Source: usually bridging veins (chronic) or cortical contusions (acute).",
body))
story.append(sp(6))
story.append(Paragraph("Key Facts", sub_head))
story.append(info_table([
("Incidence", "<b>5% of all head injuries; 50% of fatal head injuries</b>"),
("Source (acute)","Cortical contusions → arterial bleeding"),
("Source (chronic)","<b>Bridging veins</b> torn by rotational acceleration/deceleration of brain"),
("External signs","<b>Often NO external manifestation</b> — scalp may look normal"),
("Shape", "Crescent-shaped; unilateral OR bilateral (diffuse)"),
("Brain effect", "Brain compressed but <b>less ironed out</b> (dura is external to the blood)"),
("At autopsy", "<b>Washed away by thin stream of water</b> (vs EDH: scraped)"),
("Lucid interval","<b>Less well defined</b> than extradural (no classic lucid interval)"),
("Alcohol", "Alcohol found in subdural hematoma at autopsy ≈ blood alcohol at time of injury"),
]))
story.append(sp(6))
story.append(Paragraph("Types by Timing", sub_head))
story.append(comparison_table(
[Paragraph("<b>Type</b>", body), Paragraph("<b>Onset</b>", body), Paragraph("<b>Features</b>", body)],
[
[Paragraph("Acute SDH", body), Paragraph("< 72 hours", body), Paragraph("Arterial; rapidly expanding; high mortality", body)],
[Paragraph("Subacute SDH", body), Paragraph("3–21 days", body), Paragraph("Mixed features", body)],
[Paragraph("Chronic SDH", body), Paragraph("> 21 days", body), Paragraph("Bridging veins; elderly/alcoholics; may present weeks after minor trauma", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Extradural vs Subdural vs Subarachnoid — Comparison", sub_head))
story.append(comparison_table(
[Paragraph("<b>Feature</b>", body), Paragraph("<b>Extradural</b>", body), Paragraph("<b>Subdural</b>", body), Paragraph("<b>Subarachnoid</b>", body)],
[
[Paragraph("Location", body), Paragraph("Skull & dura", body), Paragraph("Dura & arachnoid", body), Paragraph("Arachnoid & pia", body)],
[Paragraph("Source", body), Paragraph("Middle meningeal a.", body), Paragraph("Bridging veins / cortical", body), Paragraph("Aneurysm / trauma", body)],
[Paragraph("Incidence", body), Paragraph("2% head injuries", body), Paragraph("5%; 50% fatal", body), Paragraph("Very common", body)],
[Paragraph("Lucid interval", body), Paragraph("<b>Classic</b>", body), Paragraph("Less defined", body), Paragraph("Variable", body)],
[Paragraph("At autopsy", body), Paragraph("Scraped", body), Paragraph("<b>Washed by water</b>", body), Paragraph("Not washable", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Medicolegal Importance", sub_head))
for pt in [
"<b>Shaken Baby Syndrome</b>: SDH + retinal hemorrhages + no external injury → acceleration-deceleration",
"Chronic SDH in alcoholics / elderly — may present after trivial or no apparent trauma",
"Can be non-traumatic (spontaneous) — aneurysm, anticoagulant therapy, tumor",
"Domestic violence, child abuse, assault cases",
"If SDH present at autopsy: fresh → preserve portion for <b>alcohol and drugs analysis</b>",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 8. WASHERWOMAN'S HAND
# ════════════════════════════════════════════════════════════════
story.append(section_banner("8", "WASHERWOMAN'S HAND", "🫶"))
story.append(sp(8))
ww_imgs = two_images("washer_hand", "Fig. Washerwoman's hand in drowning — sodden, wrinkled, white skin", "washer_foot", "Fig. Washerwoman's foot — similar changes extend to feet", 6.5*cm)
story.append(ww_imgs)
story.append(sp(8))
story.append(Paragraph("Definition", sub_head))
story.append(Paragraph(
"A post-mortem change seen in <b>drowning</b> (body immersed in water): absorption of water into the outer layer "
"(stratum corneum) of the skin causes maceration → the skin becomes <b>sodden, thickened, wrinkled and white/pale</b>, "
"resembling the hands of a person who has been washing clothes for prolonged periods.",
body))
story.append(sp(6))
story.append(Paragraph("Timeline of Skin Changes", sub_head))
story.append(comparison_table(
[Paragraph("<b>Time of Immersion</b>", body), Paragraph("<b>Change Observed</b>", body)],
[
[Paragraph("2–4 hours", body), Paragraph("First changes on <b>fingertips</b>", body)],
[Paragraph("4–8 hours", body), Paragraph("Bleaching of epidermis begins; spreads to palm, back of fingers", body)],
[Paragraph("~20 hours", body), Paragraph("Fully established: sodden, bleached, wrinkled skin ('washerwoman's hands')", body)],
[Paragraph("~24 hours", body), Paragraph("Spreads to backs of hands; similar changes on feet", body)],
[Paragraph("Clothed areas", body), Paragraph("Changes appear later (protected by clothing); feet if wearing shoes: ~2× as long", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Associated Post-Mortem Signs of Drowning", sub_head))
froth_img = img_block("froth_drowning", "Fig. Copious froth from nostrils in drowning", 6*cm)
drowning_pts = [
Paragraph("<b>External signs:</b>", sub_head),
] + bullet_list([
"Copious <b>white frothy foam</b> from mouth and nostrils (emphysema aquosum)",
"Cutis anserina (goose skin) — pilomotor contraction from cold",
"<b>Cadaveric spasm</b> — clutching weeds, grass, twigs in hands (proves alive when entered water)",
"Postmortem lividity: <b>light pink</b> (unreduced hemoglobin in cold)",
"Skin wet, cold, pale; mud/algae/sand in body orifices",
]) + [
Paragraph("<b>Internal signs:</b>", sub_head),
] + bullet_list([
"<b>Paltauf's hemorrhages</b>: pale pink/bluish-red subpleural hemorrhages (50% of cases)",
"Lungs: voluminous, ballooned, overinflated — frothy fluid on cut section",
"Fresh water: lungs pale, light; Sea water: lungs heavy (~2 kg), purplish",
"Frothy fluid/water in airways and stomach",
"Diatom test: diatoms in lung/bone marrow confirms drowning (antemortem immersion)",
])
froth_col = Table([[froth_img]], colWidths=[7*cm])
pt_col = Table([[p] for p in drowning_pts], colWidths=[9.5*cm])
pt_col.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),1)]))
side2 = Table([[pt_col, froth_col]], colWidths=[10*cm, 7*cm])
side2.setStyle(TableStyle([("VALIGN",(0,0),(-1,-1),"TOP")]))
story.append(side2)
story.append(sp(6))
story.append(Paragraph("Medicolegal Importance", sub_head))
for pt in [
"Indicates duration of immersion in water — valuable for establishing timeline",
"Does NOT prove cause of death as drowning (body may have been thrown in post-mortem)",
"Must correlate with other signs: froth, Paltauf's hemorrhages, diatom test, lung findings",
"Cadaveric spasm is the strongest evidence that victim was <b>alive</b> on entering water",
"Postmortem submersion: no froth, no washerwoman's changes initially; no cadaveric spasm",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════
# 9. HAMMER
# ════════════════════════════════════════════════════════════════
story.append(section_banner("9", "HAMMER (Blunt Weapon)", "🔨"))
story.append(sp(8))
story.append(Paragraph("Description", sub_head))
story.append(Paragraph(
"A <b>blunt weapon</b> with a heavy metal head (flat striking face + claw or peen) and a handle. "
"Produces characteristic <b>patterned blunt force injuries</b>. "
"Commonly used in assaults — associated with <b>homicidal head injuries</b>.",
body))
story.append(sp(6))
story.append(Paragraph("Injuries Produced", sub_head))
story.append(info_table([
("Abrasion", "Imprint abrasion matching shape of hammer face — rectangular/circular patterned mark"),
("Contusion (bruise)", "<b>Patterned contusion</b> — shape of striking surface; may show outline of hammer head"),
("Laceration", "Wound with <b>bruised, crushed, contused margins</b>; tissue bridging across wound; hair bulbs crushed"),
("Skull fracture", "<b>Depressed/comminuted fracture</b> — 'pond fracture' or 'spider-web' pattern matching hammer face"),
("Brain injury", "Extradural hematoma (from skull fracture + meningeal artery tear); cerebral contusion; DAI"),
]))
story.append(sp(6))
story.append(Paragraph("Key Features of Blunt Force Wounds (vs Sharp Force)", sub_head))
story.append(comparison_table(
[Paragraph("<b>Feature</b>", body), Paragraph("<b>Blunt (Hammer)</b>", body), Paragraph("<b>Sharp (Knife)</b>", body)],
[
[Paragraph("Wound edges", body), Paragraph("<b>Irregular, bruised, crushed</b>", body), Paragraph("Clean, sharp, no bruising", body)],
[Paragraph("Tissue bridging", body), Paragraph("<b>Present</b>", body), Paragraph("Absent", body)],
[Paragraph("Hair bulbs", body), Paragraph("<b>Crushed</b>", body), Paragraph("Cut cleanly", body)],
[Paragraph("Shape", body), Paragraph("May be patterned (matches weapon)", body), Paragraph("Linear or slit-shaped", body)],
[Paragraph("Surrounding area", body), Paragraph("Ecchymosis, swelling", body), Paragraph("No bruising", body)],
[Paragraph("Deeper tissue", body), Paragraph("Extensive crushing, fractures", body), Paragraph("Track follows blade path", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Manner of Death with Hammer", sub_head))
story.append(comparison_table(
[Paragraph("<b>Manner</b>", body), Paragraph("<b>Features</b>", body)],
[
[Paragraph("Homicidal", body), Paragraph("Multiple blows; different directions; inaccessible areas (occiput, back of head); defense injuries on hands/arms", body)],
[Paragraph("Accidental", body), Paragraph("Single blow; scene evidence (work accident, fall onto hammer)", body)],
[Paragraph("Suicidal", body), Paragraph("Extremely rare with a hammer", body)],
]
))
story.append(sp(6))
story.append(Paragraph("Medicolegal Importance", sub_head))
for pt in [
"Shape of wound/fracture pattern may <b>identify the specific weapon</b>",
"Intermediate object between hammer and head (hair, clothing) → modifies pattern",
"Multiple wounds with different angles → <b>multiple blows from multiple positions</b> → confirms assault",
"Distinguish from fall: fall usually produces a single impact point; assault produces multiple",
"Patterned wound → tool mark evidence → match to hammer recovered at scene",
"Blood spatter pattern helps reconstruct events",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(sp(12))
story.append(hr())
story.append(sp(6))
end_style = make_style("EndNote", fontSize=9, textColor=colors.grey, leading=14, alignment=TA_CENTER, fontName="Helvetica-Oblique")
story.append(Paragraph("— End of FMT Spotter Guide —", end_style))
story.append(Paragraph("Sources: KS Narayan Reddy's Essentials of Forensic Medicine & Toxicology (36th Ed. 2026) · Parikh's Textbook · PC Dikshit's Textbook", end_style))
story.append(Paragraph("Compiled by Orris AI Assistant | For Academic Use Only", end_style))
# ── build PDF ────────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=cover_page, onLaterPages=on_page)
print(f"PDF built → {OUTPUT_PATH}")
cd /home/daytona/workspace/fmt-spotter-guide && python3 build_pdf.py 2>&1
ls -lh /home/daytona/workspace/fmt-spotter-guide/ && python3 -c "from pypdf import PdfReader; r=PdfReader('/home/daytona/workspace/fmt-spotter-guide/FMT_Spotter_Guide.pdf'); print(f'Pages: {len(r.pages)}'); print('Title:', r.metadata.title)"
FMT Spotter Guide
PDF Document · PDF
| # | Spotter | Images |
|---|---|---|
| 1 | Liver Cirrhosis | Autopsy descriptions + comparison tables |
| 2 | Russell's Viper | Actual photo of Russell's viper + cobra vs viper comparison table |
| 3 | Summons / Subpoena | Full legal framework + punishment table |
| 4 | Brain Knife | Description, uses at autopsy, instrument comparison table |
| 5 | Castor (Ricinus communis) | Plant photo + seeds photo + poisoning features |
| 6 | Rifle Bullet | Entry wound photo + rifled bore diagram + entry vs exit table |
| 7 | Subdural Hemorrhage | 2 SDH photos + EDH vs SDH vs SAH comparison |
| 8 | Washerwoman's Hand | Hand + foot maceration photos + drowning signs + Paltauf's |
| 9 | Hammer | Blunt vs sharp wound table + manner of death analysis |