Essay 15 mark and short and 5 mark refer fm book of mbbs answer all these
Generate a study guide PDF on fingerprints and dactylography with classification diagrams
ls -la /tmp/workspace/ 2>/dev/null || mkdir -p /tmp/workspace && echo "workspace ready"
mkdir -p /tmp/workspace/fingerprint-study-guide && echo "dir created"
~/fingerprint-study-guide/generate_pdf.py
"""
Fingerprints & Dactylography - MBBS FM Study Guide PDF
Uses reportlab for full PDF generation with diagrams drawn via canvas
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import Flowable
from reportlab.graphics.shapes import Drawing, Rect, Circle, Ellipse, Line, String, Polygon, Path
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import Group
import math
OUTPUT = "/tmp/workspace/fingerprint-study-guide/Fingerprints_Dactylography_Study_Guide.pdf"
# ─── COLOURS ─────────────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1A237E")
MID_BLUE = colors.HexColor("#283593")
ACCENT_BLUE = colors.HexColor("#3949AB")
LIGHT_BLUE = colors.HexColor("#E8EAF6")
TEAL = colors.HexColor("#00695C")
LIGHT_TEAL = colors.HexColor("#E0F2F1")
ORANGE = colors.HexColor("#E65100")
LIGHT_ORANGE= colors.HexColor("#FFF3E0")
PURPLE = colors.HexColor("#4A148C")
LIGHT_PURPLE= colors.HexColor("#F3E5F5")
RED = colors.HexColor("#B71C1C")
GREEN = colors.HexColor("#1B5E20")
LIGHT_GREEN = colors.HexColor("#E8F5E9")
GOLD = colors.HexColor("#F57F17")
LIGHT_GOLD = colors.HexColor("#FFFDE7")
WHITE = colors.white
BLACK = colors.black
GREY = colors.HexColor("#455A64")
LIGHT_GREY = colors.HexColor("#ECEFF1")
DARK_GREY = colors.HexColor("#263238")
# ─── STYLES ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent='Normal', **kwargs):
s = ParagraphStyle(name, parent=styles[parent])
for k, v in kwargs.items():
setattr(s, k, v)
return s
cover_title = make_style('CoverTitle', fontSize=30, textColor=WHITE, alignment=TA_CENTER, leading=38, fontName='Helvetica-Bold')
cover_sub = make_style('CoverSub', fontSize=15, textColor=colors.HexColor("#C5CAE9"), alignment=TA_CENTER, leading=22, fontName='Helvetica')
cover_tag = make_style('CoverTag', fontSize=10, textColor=colors.HexColor("#9FA8DA"), alignment=TA_CENTER, leading=14)
h1_style = make_style('H1Style', fontSize=16, textColor=WHITE, alignment=TA_LEFT, leading=22, fontName='Helvetica-Bold', spaceAfter=4)
h2_style = make_style('H2Style', fontSize=13, textColor=DARK_BLUE, alignment=TA_LEFT, leading=18, fontName='Helvetica-Bold', spaceBefore=10, spaceAfter=4)
h3_style = make_style('H3Style', fontSize=11, textColor=TEAL, alignment=TA_LEFT, leading=16, fontName='Helvetica-Bold', spaceBefore=8, spaceAfter=3)
body_style = make_style('BodyStyle', fontSize=9.5, textColor=DARK_GREY, alignment=TA_JUSTIFY, leading=15, fontName='Helvetica', spaceBefore=2, spaceAfter=2)
bullet_style = make_style('BulletStyle', fontSize=9.5, textColor=DARK_GREY, alignment=TA_LEFT, leading=14, fontName='Helvetica', leftIndent=14, spaceBefore=1, spaceAfter=1)
bold_style = make_style('BoldStyle', fontSize=9.5, textColor=DARK_GREY, alignment=TA_LEFT, leading=14, fontName='Helvetica-Bold', spaceBefore=2, spaceAfter=2)
note_style = make_style('NoteStyle', fontSize=9, textColor=RED, alignment=TA_LEFT, leading=13, fontName='Helvetica-BoldOblique', spaceBefore=2, spaceAfter=4, leftIndent=8)
caption_style = make_style('Caption', fontSize=8.5, textColor=GREY, alignment=TA_CENTER, leading=12, fontName='Helvetica-Oblique')
small_style = make_style('Small', fontSize=8.5, textColor=DARK_GREY, alignment=TA_LEFT, leading=12, fontName='Helvetica')
keyword_style = make_style('Keyword', fontSize=9.5, textColor=PURPLE, alignment=TA_LEFT, leading=14, fontName='Helvetica-Bold')
imp_style = make_style('Important', fontSize=9.5, textColor=ORANGE, alignment=TA_LEFT, leading=14, fontName='Helvetica-Bold', leftIndent=8)
# ─── CUSTOM FLOWABLES ────────────────────────────────────────────────────────
class SectionHeader(Flowable):
"""Full-width colour band with section title."""
def __init__(self, text, bg=DARK_BLUE, fg=WHITE, height=28):
super().__init__()
self.text = text
self.bg = bg
self.fg = fg
self.height = height
self.width = 0 # set by wrap
def wrap(self, availW, availH):
self.width = availW
return availW, self.height
def draw(self):
c = self.canv
# background
c.setFillColor(self.bg)
c.roundRect(0, 0, self.width, self.height, 6, fill=1, stroke=0)
# text
c.setFillColor(self.fg)
c.setFont('Helvetica-Bold', 13)
c.drawString(12, 8, self.text)
class ColorBox(Flowable):
"""Coloured info/warning box with text."""
def __init__(self, text, bg=LIGHT_BLUE, border=ACCENT_BLUE, style=None, padding=8):
super().__init__()
self.text = text
self.bg = bg
self.border = border
self.style = style or body_style
self.padding = padding
self._para = None
self.width = 0
def wrap(self, availW, availH):
self.width = availW
inner = availW - self.padding * 2 - 6 # 3px left border
self._para = Paragraph(self.text, self.style)
w, h = self._para.wrap(inner, availH)
self._h = h + self.padding * 2
return availW, self._h
def draw(self):
c = self.canv
c.setFillColor(self.bg)
c.roundRect(4, 0, self.width - 4, self._h, 4, fill=1, stroke=0)
c.setFillColor(self.border)
c.rect(0, 0, 4, self._h, fill=1, stroke=0)
self._para.drawOn(c, 4 + self.padding, self.padding)
# ─── FINGERPRINT PATTERN DIAGRAMS ────────────────────────────────────────────
def draw_loop(c, x, y, w, h, label, sublabel="", loopdir="right"):
"""Draw a loop fingerprint pattern."""
# Background
c.setFillColor(colors.HexColor("#F8F9FA"))
c.setStrokeColor(colors.HexColor("#90A4AE"))
c.setLineWidth(0.5)
c.roundRect(x, y, w, h, 5, fill=1, stroke=1)
cx, cy = x + w/2, y + h/2 - 8
scale_x, scale_y = w * 0.35, h * 0.35
# Draw concentric loops (curved lines going one way)
c.setStrokeColor(DARK_BLUE)
c.setLineWidth(1.2)
direction = 1 if loopdir == "right" else -1
for i in range(4, 0, -1):
factor = i / 4.0
# open loop shape
p = c.beginPath()
steps = 40
for j in range(steps + 1):
t = math.pi * j / steps # 0 to pi
px = cx + direction * scale_x * factor * math.cos(t - math.pi/2) * 0.8
py = cy + scale_y * factor * math.sin(t - math.pi/2) + scale_y * factor * 0.4
if j == 0:
p.moveTo(px, py)
else:
p.lineTo(px, py)
c.drawPath(p, fill=0, stroke=1)
# Delta point
c.setFillColor(RED)
delta_x = cx - direction * scale_x * 0.7
delta_y = cy - scale_y * 0.5
c.circle(delta_x, delta_y, 2.5, fill=1, stroke=0)
# Labels
c.setFillColor(DARK_BLUE)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(x + w/2, y + h - 14, label)
c.setFont("Helvetica", 7.5)
c.setFillColor(GREY)
c.drawCentredString(x + w/2, y + 4, sublabel)
def draw_whorl(c, x, y, w, h, label, sublabel=""):
"""Draw a whorl fingerprint pattern."""
c.setFillColor(colors.HexColor("#F8F9FA"))
c.setStrokeColor(colors.HexColor("#90A4AE"))
c.setLineWidth(0.5)
c.roundRect(x, y, w, h, 5, fill=1, stroke=1)
cx, cy = x + w/2, y + h/2 - 8
c.setStrokeColor(TEAL)
c.setLineWidth(1.2)
# Concentric ellipses (whorls)
for i in range(1, 5):
rw = w * 0.07 * i
rh = h * 0.08 * i
c.ellipse(cx - rw, cy - rh, cx + rw, cy + rh, fill=0, stroke=1)
# Two delta points
c.setFillColor(RED)
c.circle(cx - w * 0.33, cy - h * 0.15, 2.5, fill=1, stroke=0)
c.circle(cx + w * 0.33, cy - h * 0.15, 2.5, fill=1, stroke=0)
c.setFillColor(TEAL)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(x + w/2, y + h - 14, label)
c.setFont("Helvetica", 7.5)
c.setFillColor(GREY)
c.drawCentredString(x + w/2, y + 4, sublabel)
def draw_arch(c, x, y, w, h, label, sublabel="", tented=False):
"""Draw an arch fingerprint pattern."""
c.setFillColor(colors.HexColor("#F8F9FA"))
c.setStrokeColor(colors.HexColor("#90A4AE"))
c.setLineWidth(0.5)
c.roundRect(x, y, w, h, 5, fill=1, stroke=1)
cx, cy = x + w/2, y + h/2 - 8
c.setStrokeColor(ORANGE)
c.setLineWidth(1.2)
for i in range(1, 5):
factor = i / 5.0
peak_h = (h * 0.22 * (5 - i)) if not tented else (h * 0.35 * (1 if i == 1 else 0.5))
p = c.beginPath()
p.moveTo(x + 6, cy - h * 0.1)
if tented and i == 1:
p.lineTo(cx, cy + peak_h)
p.lineTo(x + w - 6, cy - h * 0.1)
else:
ctrl_y = cy + peak_h
p.curveTo(cx - w*0.2, ctrl_y, cx + w*0.2, ctrl_y, x + w - 6, cy - h * 0.1)
c.drawPath(p, fill=0, stroke=1)
# No delta for plain arch; one delta for tented
if tented:
c.setFillColor(RED)
c.circle(cx, cy + h * 0.15, 2.5, fill=1, stroke=0)
c.setFillColor(ORANGE)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(x + w/2, y + h - 14, label)
c.setFont("Helvetica", 7.5)
c.setFillColor(GREY)
c.drawCentredString(x + w/2, y + 4, sublabel)
def draw_composite(c, x, y, w, h, label, sublabel=""):
"""Draw a composite (twin loop) fingerprint pattern."""
c.setFillColor(colors.HexColor("#F8F9FA"))
c.setStrokeColor(colors.HexColor("#90A4AE"))
c.setLineWidth(0.5)
c.roundRect(x, y, w, h, 5, fill=1, stroke=1)
cx, cy = x + w/2, y + h/2 - 8
c.setStrokeColor(PURPLE)
c.setLineWidth(1.2)
# Left loop
for i in range(3, 0, -1):
factor = i / 3.0
p = c.beginPath()
steps = 30
for j in range(steps + 1):
t = math.pi * j / steps
px = cx - w*0.12 + (-w*0.18*factor) * math.cos(t - math.pi/2) * 0.7
py = cy + (h*0.2*factor) * math.sin(t - math.pi/2) + h*0.08*factor
if j == 0: p.moveTo(px, py)
else: p.lineTo(px, py)
c.drawPath(p, fill=0, stroke=1)
# Right loop
for i in range(3, 0, -1):
factor = i / 3.0
p = c.beginPath()
steps = 30
for j in range(steps + 1):
t = math.pi * j / steps
px = cx + w*0.12 + (w*0.18*factor) * math.cos(t - math.pi/2) * 0.7
py = cy + (h*0.2*factor) * math.sin(t - math.pi/2) + h*0.08*factor
if j == 0: p.moveTo(px, py)
else: p.lineTo(px, py)
c.drawPath(p, fill=0, stroke=1)
# Two deltas
c.setFillColor(RED)
c.circle(cx - w*0.38, cy - h*0.2, 2.5, fill=1, stroke=0)
c.circle(cx + w*0.38, cy - h*0.2, 2.5, fill=1, stroke=0)
c.setFillColor(PURPLE)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(x + w/2, y + h - 14, label)
c.setFont("Helvetica", 7.5)
c.setFillColor(GREY)
c.drawCentredString(x + w/2, y + 4, sublabel)
class FingerprintDiagram(Flowable):
"""4-panel fingerprint classification diagram."""
WIDTH = 480
HEIGHT = 165
def wrap(self, availW, availH):
self._availW = availW
return availW, self.HEIGHT + 30
def draw(self):
c = self.canv
W = min(self._availW, self.WIDTH)
panel_w = W / 4 - 6
panel_h = self.HEIGHT
# Title bar
c.setFillColor(DARK_BLUE)
c.roundRect(0, panel_h + 4, W, 24, 4, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 10)
c.drawCentredString(W/2, panel_h + 11, "CLASSIFICATION OF FINGERPRINT PATTERNS")
# Legend: delta dot
c.setFillColor(RED)
c.circle(W - 80, panel_h + 14, 4, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica", 8)
c.drawString(W - 73, panel_h + 11, "= Delta point")
offsets = [0, panel_w + 6, (panel_w + 6)*2, (panel_w + 6)*3]
draw_loop (c, offsets[0], 0, panel_w, panel_h, "LOOP", "60-70% | Radial / Ulnar", loopdir="right")
draw_whorl (c, offsets[1], 0, panel_w, panel_h, "WHORL", "25-35% | Concentric / Spiral")
draw_arch (c, offsets[2], 0, panel_w, panel_h, "ARCH", "6-7% | Plain / Tented", tented=False)
draw_composite (c, offsets[3], 0, panel_w, panel_h, "COMPOSITE", "1-2% | Twin / Pocket")
class LoopSubtypesDiagram(Flowable):
"""Shows radial vs ulnar loops side by side."""
WIDTH = 300
HEIGHT = 130
def wrap(self, availW, availH):
self._availW = availW
return availW, self.HEIGHT + 28
def draw(self):
c = self.canv
W = min(self._availW, self.WIDTH)
panel_w = W / 2 - 6
panel_h = self.HEIGHT
c.setFillColor(ACCENT_BLUE)
c.roundRect(0, panel_h + 4, W, 22, 4, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(W/2, panel_h + 9, "LOOP SUBTYPES")
draw_loop(c, 0, 0, panel_w, panel_h, "ULNAR LOOP", "Opens towards ulnar side", loopdir="right")
draw_loop(c, panel_w + 8, 0, panel_w, panel_h, "RADIAL LOOP", "Opens towards radial side", loopdir="left")
class WhorlSubtypesDiagram(Flowable):
"""Shows whorl subtypes."""
WIDTH = 440
HEIGHT = 130
def wrap(self, availW, availH):
self._availW = availW
return availW, self.HEIGHT + 28
def draw(self):
c = self.canv
W = min(self._availW, self.WIDTH)
panel_w = W / 4 - 5
panel_h = self.HEIGHT
c.setFillColor(TEAL)
c.roundRect(0, panel_h + 4, W, 22, 4, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(W/2, panel_h + 9, "WHORL SUBTYPES")
subtypes = ["Concentric", "Spiral", "Double Spiral", "Almond-Shaped"]
for i, name in enumerate(subtypes):
x = i * (panel_w + 5)
draw_whorl(c, x, 0, panel_w, panel_h, name, "")
class DeltaExplained(Flowable):
"""Explains delta point with diagram."""
WIDTH = 280
HEIGHT = 120
def wrap(self, availW, availH):
self._availW = availW
return self.WIDTH, self.HEIGHT + 26
def draw(self):
c = self.canv
W, H = self.WIDTH, self.HEIGHT
c.setFillColor(LIGHT_GOLD)
c.setStrokeColor(GOLD)
c.setLineWidth(1)
c.roundRect(0, 0, W, H + 24, 6, fill=1, stroke=1)
c.setFillColor(GOLD)
c.roundRect(0, H, W, 24, 6, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(W/2, H + 8, "DELTA POINT (TRIRADIUS)")
# Simple delta diagram
cx, cy = W * 0.35, H * 0.42
# Three ridge systems meeting
c.setStrokeColor(DARK_BLUE)
c.setLineWidth(1.2)
for angle in [30, 150, 270]:
rad = math.radians(angle)
ex = cx + 40 * math.cos(rad)
ey = cy + 40 * math.sin(rad)
c.line(cx, cy, ex, ey)
# Ridge lines around delta
c.setLineWidth(0.8)
c.setStrokeColor(colors.HexColor("#78909C"))
for offset in [-8, 8]:
c.line(cx - 35, cy + offset, cx - 5, cy)
c.line(cx + 5, cy, cx + 35, cy + offset)
c.setFillColor(RED)
c.circle(cx, cy, 4, fill=1, stroke=0)
# Annotation
c.setFillColor(DARK_GREY)
c.setFont("Helvetica-Bold", 8)
c.drawString(cx + 8, cy + 2, "Delta")
c.setFont("Helvetica", 7.5)
c.drawString(W * 0.55, H * 0.75, "Point where 3 ridge")
c.drawString(W * 0.55, H * 0.60, "systems meet")
c.drawString(W * 0.55, H * 0.45, "• Loops: 1 delta")
c.drawString(W * 0.55, H * 0.30, "• Whorls: 2 deltas")
c.drawString(W * 0.55, H * 0.15, "• Arches: 0 deltas")
class HistoryTimeline(Flowable):
"""Historical timeline of dactylography."""
WIDTH = 490
HEIGHT = 70
def wrap(self, availW, availH):
self._availW = availW
return availW, self.HEIGHT + 10
def draw(self):
c = self.canv
W = min(self._availW, self.WIDTH)
H = self.HEIGHT
events = [
("1858", "Herschel\n(India, W. Bengal)", DARK_BLUE),
("1880", "Faulds\n(Japan - 1st paper)", TEAL),
("1892", "Galton\n(Systematized)", ACCENT_BLUE),
("1893", "Henry\n(Classification)", PURPLE),
("1897", "Kolkata\n(1st Bureau)", ORANGE),
("1900", "Scotland Yard\n(UK adopted)", GREEN),
]
n = len(events)
spacing = (W - 30) / (n - 1)
# Timeline line
c.setStrokeColor(colors.HexColor("#90A4AE"))
c.setLineWidth(2)
c.line(15, H * 0.55, W - 15, H * 0.55)
for i, (year, label, col) in enumerate(events):
ex = 15 + i * spacing
# Vertical tick
c.setStrokeColor(col)
c.setLineWidth(1.5)
c.line(ex, H * 0.45, ex, H * 0.65)
# Dot
c.setFillColor(col)
c.circle(ex, H * 0.55, 5, fill=1, stroke=0)
# Year above
c.setFillColor(col)
c.setFont("Helvetica-Bold", 8)
c.drawCentredString(ex, H * 0.75, year)
# Label below
c.setFillColor(DARK_GREY)
c.setFont("Helvetica", 6.8)
lines = label.split("\n")
for j, line in enumerate(lines):
c.drawCentredString(ex, H * 0.30 - j * 9, line)
class GaltonHenryChart(Flowable):
"""Galton-Henry classification tree."""
WIDTH = 490
HEIGHT = 140
def wrap(self, availW, availH):
self._availW = availW
return availW, self.HEIGHT + 34
def draw(self):
c = self.canv
W = min(self._availW, self.WIDTH)
H = self.HEIGHT
# Title
c.setFillColor(DARK_BLUE)
c.roundRect(0, H, W, 32, 4, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 10)
c.drawCentredString(W/2, H + 18, "GALTON-HENRY CLASSIFICATION SYSTEM")
c.setFont("Helvetica", 8)
c.drawCentredString(W/2, H + 6, "Fingerprints are classified into 4 main types with subtypes")
# Root
root_x, root_y = W/2, H - 18
c.setFillColor(DARK_BLUE)
c.roundRect(root_x - 60, root_y - 12, 120, 22, 4, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(root_x, root_y, "FINGERPRINT PATTERNS")
main_types = [
("LOOPS\n60-70%", DARK_BLUE, ["Radial Loop", "Ulnar Loop"]),
("WHORLS\n25-35%", TEAL, ["Concentric", "Spiral", "Dbl Spiral", "Almond"]),
("ARCHES\n6-7%", ORANGE, ["Plain Arch", "Tented Arch", "Exceptional"]),
("COMPOSITES\n1-2%", PURPLE, ["Central Pocket", "Lateral Pocket", "Twinned", "Accidental"]),
]
branch_y = H - 65
leaf_y = H - 125
xs = [W*0.12, W*0.35, W*0.62, W*0.88]
for i, (label, col, subtypes) in enumerate(main_types):
bx = xs[i]
# Line from root to branch
c.setStrokeColor(col)
c.setLineWidth(1.2)
c.line(root_x, root_y - 12, bx, branch_y + 14)
# Branch box
c.setFillColor(col)
lines = label.split("\n")
bw = max(len(l)*6 for l in lines) + 12
c.roundRect(bx - bw/2, branch_y - 2, bw, 24, 3, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 7.5)
c.drawCentredString(bx, branch_y + 10, lines[0])
c.setFont("Helvetica", 6.5)
c.drawCentredString(bx, branch_y + 1, lines[1] if len(lines) > 1 else "")
# Leaves
n = len(subtypes)
span = min(bw * 1.4, 110)
for j, sub in enumerate(subtypes):
if n == 1:
lx = bx
else:
lx = bx - span/2 + j * span/(n-1)
c.setStrokeColor(colors.HexColor("#90A4AE"))
c.setLineWidth(0.8)
c.line(bx, branch_y - 2, lx, leaf_y + 12)
lw = max(len(sub)*5.5, 40)
c.setFillColor(colors.HexColor("#E3F2FD") if col == DARK_BLUE else
colors.HexColor("#E0F2F1") if col == TEAL else
colors.HexColor("#FFF3E0") if col == ORANGE else
colors.HexColor("#F3E5F5"))
c.setStrokeColor(col)
c.setLineWidth(0.5)
c.roundRect(lx - lw/2, leaf_y, lw, 14, 2, fill=1, stroke=1)
c.setFillColor(DARK_GREY)
c.setFont("Helvetica", 6.5)
c.drawCentredString(lx, leaf_y + 4, sub)
class MedicolegalTable(Flowable):
"""Summary table of medicolegal importance."""
pass # will use normal Table
# ─── BUILD PDF ────────────────────────────────────────────────────────────────
def build_pdf():
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=18*mm,
rightMargin=18*mm,
topMargin=15*mm,
bottomMargin=15*mm,
title="Fingerprints & Dactylography - MBBS FM Study Guide",
author="Orris Medical AI",
subject="Forensic Medicine - Identification",
)
story = []
W = A4[0] - 36*mm # usable width
# ─── COVER PAGE ─────────────────────────────────────────────────────────
class CoverPage(Flowable):
def wrap(self, availW, availH):
self._w = availW
return availW, A4[1] - 30*mm
def draw(self):
c = self.canv
W, H = self._w, A4[1] - 30*mm
# Deep blue gradient background simulation
for i in range(20):
f = i / 20.0
r = 0.10 + 0.05*f
g = 0.14 + 0.03*f
b = 0.49 + 0.06*f
c.setFillColorRGB(r, g, b)
c.rect(0, H*(1 - f/20*1.1), W, H/19, fill=1, stroke=0)
# Dark overlay at bottom
c.setFillColor(colors.HexColor("#0D1B6E"))
c.rect(0, 0, W, H*0.28, fill=1, stroke=0)
# Decorative circles
c.setFillColor(colors.HexColor("#3F51B5"))
c.setStrokeColor(colors.white)
c.setLineWidth(0)
c.circle(W*0.85, H*0.78, 55, fill=1, stroke=0)
c.circle(W*0.15, H*0.22, 40, fill=1, stroke=0)
# Fingerprint icon (simplified whorl)
c.setStrokeColor(colors.HexColor("#7986CB"))
c.setLineWidth(2)
for r in [15, 22, 30, 38, 46]:
c.circle(W*0.85, H*0.78, r, fill=0, stroke=1)
# Title text
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 32)
c.drawCentredString(W/2, H*0.70, "FINGERPRINTS")
c.setFont("Helvetica-Bold", 22)
c.setFillColor(colors.HexColor("#C5CAE9"))
c.drawCentredString(W/2, H*0.62, "& DACTYLOGRAPHY")
# Divider
c.setStrokeColor(colors.HexColor("#7986CB"))
c.setLineWidth(1.5)
c.line(W*0.15, H*0.58, W*0.85, H*0.58)
c.setFillColor(colors.HexColor("#9FA8DA"))
c.setFont("Helvetica", 13)
c.drawCentredString(W/2, H*0.52, "Complete MBBS Forensic Medicine Study Guide")
c.setFont("Helvetica", 11)
c.drawCentredString(W/2, H*0.46, "Chapter 3: Identification | Pre-Final Year MBBS")
# Bottom bar
c.setFillColor(colors.HexColor("#7986CB"))
c.roundRect(W*0.1, H*0.38, W*0.8, 1, 2, fill=1, stroke=0)
# Topics preview
topics = [
"History & Principles", "Classification (Loops / Whorls / Arches / Composites)",
"Types of Prints", "Development Methods", "Properties", "Medicolegal Importance",
"Galton-Henry System", "Poroscopy", "DNA vs Fingerprints"
]
c.setFillColor(colors.HexColor("#C5CAE9"))
c.setFont("Helvetica", 9)
for i, topic in enumerate(topics):
row, col = divmod(i, 3)
tx = W * (0.15 + col * 0.3)
ty = H * 0.33 - row * 14
c.circle(tx - 8, ty + 3, 2, fill=1, stroke=0)
c.drawString(tx - 4, ty, topic)
# Footer
c.setFillColor(colors.HexColor("#9FA8DA"))
c.setFont("Helvetica", 8)
c.drawCentredString(W/2, H*0.04, "Based on: Essentials of FMT (Pillay) | P.C. Dikshit | Parikh's Textbook")
c.setFont("Helvetica-Bold", 9)
c.setFillColor(colors.white)
c.drawCentredString(W/2, H*0.10, "Exam Frequency: ★★★★★★★★ (Most Repeated Topic)")
story.append(CoverPage())
story.append(PageBreak())
# ─── PAGE 2: DEFINITION + HISTORY ───────────────────────────────────────
story.append(SectionHeader("1. DEFINITION & HISTORICAL BACKGROUND", bg=DARK_BLUE))
story.append(Spacer(1, 6))
story.append(ColorBox(
"<b>DACTYLOGRAPHY</b> (also called <b>Dermatoglyphics</b> or the <b>Galton-Henry System</b>) is "
"the scientific study of fingerprint patterns on the digits (fingers and thumbs) for the purpose "
"of <b>personal identification</b>. The term comes from Greek: <i>daktylos</i> (finger) + "
"<i>graphein</i> (to write/record).",
bg=LIGHT_BLUE, border=DARK_BLUE
))
story.append(Spacer(1, 8))
story.append(Paragraph("Historical Timeline", h2_style))
story.append(HistoryTimeline())
story.append(Spacer(1, 10))
hist_data = [
["Year", "Person/Event", "Contribution"],
["1823", "Purkinje (Czech)", "First described 9 basic fingerprint configurations"],
["1858", "Sir William Herschel (Bengal, India)", "First practical use for identification in India"],
["1880", "Dr. Henry Faulds (Japan)", "First scientific paper suggesting fingerprints for crime detection"],
["1892", "Sir Francis Galton (UK)", "Systematized the method; proved uniqueness & permanence"],
["1893", "Sir Edward Henry (Bengal)", "Developed the classification system (Galton-Henry system)"],
["1897", "Fingerprint Bureau (Kolkata)", "First fingerprint bureau in the world established in India"],
["1900", "Scotland Yard (UK)", "First police force to officially adopt fingerprinting"],
["1904", "USA (St. Louis)", "Fingerprinting introduced at World's Fair; adopted by FBI by 1924"],
]
t = Table(hist_data, colWidths=[1.5*cm, 6*cm, 8.5*cm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1), 'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#90A4AE")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING',(0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(t)
story.append(Spacer(1, 6))
story.append(ColorBox(
"★ <b>Exam Point</b>: Fingerprint Bureau was first established in <b>Kolkata (1897)</b>. "
"Sir William Herschel first used fingerprints in India in <b>1858</b>. "
"Sir Francis Galton systematized the system in <b>1892</b>.",
bg=LIGHT_GOLD, border=GOLD, style=imp_style
))
story.append(PageBreak())
# ─── PAGE 3: PRINCIPLE + PROPERTIES ─────────────────────────────────────
story.append(SectionHeader("2. PRINCIPLE & PROPERTIES OF FINGERPRINTS", bg=TEAL))
story.append(Spacer(1, 6))
story.append(Paragraph("Formation of Ridge Patterns", h2_style))
story.append(Paragraph(
"Fingerprints are <b>impressions of patterns formed by the papillary (epidermal) ridges</b> "
"of the fingertips. These ridges develop on the volar surface of fingers, palms, toes, and soles.",
body_style))
story.append(Spacer(1, 4))
devel_data = [
["Stage", "Timeframe", "Event"],
["Initiation", "12–16 weeks IU", "Ridge patterns begin forming on fingertips"],
["Completion", "24 weeks IU", "All ridge patterns fully formed"],
["Birth", "At birth", "Fine ridge pattern visible on bulbs of all fingers"],
["Permanence", "Lifetime", "Patterns remain unchanged unless dermis is destroyed"],
["Reproducibility","Any time", "Can be reproduced exactly at any age"],
]
t2 = Table(devel_data, colWidths=[3.5*cm, 3.5*cm, 9*cm])
t2.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TEAL),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, LIGHT_TEAL]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#80CBC4")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(t2)
story.append(Spacer(1, 10))
story.append(Paragraph("Four Cardinal Properties (PUPE)", h2_style))
props = [
("Permanence", DARK_BLUE, "Once formed (24 wks IU), ridge patterns never change throughout life unless dermis is destroyed."),
("Uniqueness", TEAL, "No two individuals (including identical twins) have identical fingerprints."),
("Practicability", ORANGE, "Can be easily classified, recorded, and compared with minimum equipment."),
("Exactness", PURPLE, "Can be reproduced exactly and compared with absolute certainty."),
]
prop_data = [["Property", "Significance"]]
for name, col, desc in props:
prop_data.append([name, desc])
tp = Table(prop_data, colWidths=[3.5*cm, 12.5*cm])
tp.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1), (0,1), DARK_BLUE),
('TEXTCOLOR', (0,2), (0,2), TEAL),
('TEXTCOLOR', (0,3), (0,3), ORANGE),
('TEXTCOLOR', (0,4), (0,4), PURPLE),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#90A4AE")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 8),
]))
story.append(tp)
story.append(Spacer(1, 6))
story.append(ColorBox(
"★ <b>Exam Point (PUPE mnemonic)</b>: <b>P</b>ermanence, <b>U</b>niqueness, "
"<b>P</b>racticability, <b>E</b>xactness. Fingerprints remain unchanged even in "
"<b>identical twins</b>. They are destroyed only if the dermis is destroyed (deep burns, acid).",
bg=LIGHT_GOLD, border=GOLD, style=imp_style
))
story.append(PageBreak())
# ─── PAGE 4: CLASSIFICATION DIAGRAM ─────────────────────────────────────
story.append(SectionHeader("3. CLASSIFICATION OF FINGERPRINT PATTERNS", bg=ACCENT_BLUE))
story.append(Spacer(1, 8))
story.append(FingerprintDiagram())
story.append(Spacer(1, 12))
story.append(GaltonHenryChart())
story.append(Spacer(1, 10))
story.append(ColorBox(
"★ <b>Exam Mnemonic — LWAC</b>: <b>L</b>oops (60-70%) → <b>W</b>horls (25-35%) → "
"<b>A</b>rches (6-7%) → <b>C</b>omposites (1-2%). "
"Delta points: Arches = 0, Loops = 1, Whorls = 2, Composites = 2.",
bg=LIGHT_GOLD, border=GOLD, style=imp_style
))
story.append(PageBreak())
# ─── PAGE 5: DETAILED SUBTYPES ───────────────────────────────────────────
story.append(SectionHeader("4. DETAILED CLASSIFICATION WITH SUBTYPES", bg=DARK_BLUE))
story.append(Spacer(1, 8))
story.append(LoopSubtypesDiagram())
story.append(Spacer(1, 6))
story.append(WhorlSubtypesDiagram())
story.append(Spacer(1, 10))
# Detailed type table
story.append(Paragraph("Detailed Subtype Table", h2_style))
type_data = [
["Type", "Frequency", "Delta Points", "Subtypes", "Key Feature"],
["LOOPS", "60–70%", "1 (one side)", "Radial Loop\nUlnar Loop",
"Opens towards radial (thumb) or ulnar (little finger) side\nMost common pattern"],
["WHORLS", "25–35%", "2 (both sides)", "Concentric\nSpiral\nDouble Spiral\nAlmond-Shaped",
"Ridges form circles/spirals around central core\nSecond most common"],
["ARCHES", "6–7%", "0 (none)", "Plain Arch\nTented Arch\nExceptional",
"Ridges enter one side and exit the other\nNo delta or core; simplest pattern"],
["COMPOSITES", "1–2%", "2 (variable)", "Central Pocket Loop\nLateral Pocket Loop\nTwinned Loop\nAccidentals",
"Combination of two or more basic types\nRarest pattern"],
]
tc = Table(type_data, colWidths=[2.5*cm, 2*cm, 2.5*cm, 4*cm, 5*cm])
tc.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1), (0,1), DARK_BLUE),
('TEXTCOLOR', (0,2), (0,2), TEAL),
('TEXTCOLOR', (0,3), (0,3), ORANGE),
('TEXTCOLOR', (0,4), (0,4), PURPLE),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#90A4AE")),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(tc)
story.append(Spacer(1, 8))
# Delta explained
story.append(DeltaExplained())
story.append(PageBreak())
# ─── PAGE 6: TYPES OF PRINTS + DEVELOPMENT ───────────────────────────────
story.append(SectionHeader("5. TYPES OF FINGERPRINTS & DEVELOPMENT METHODS", bg=TEAL))
story.append(Spacer(1, 6))
story.append(Paragraph("Types of Fingerprints Found at Crime Scenes", h2_style))
print_types = [
["Type", "Description", "Surface", "Development Needed?"],
["Visible\n(Patent) Prints",
"Made in or by a contrasting material\n(blood, grease, paint, oil, dirt)",
"Any surface",
"No – directly visible\nPhotograph immediately"],
["Latent Prints",
"Invisible; left by sweat/sebaceous secretion\nMost commonly found at crime scenes",
"Non-porous (glass, metal)\nPorous (paper, cloth)",
"YES – requires development\n(see methods below)"],
["Plastic\n(Moulded) Prints",
"Impression in soft, pliable material\n3-dimensional indentation",
"Wax, putty, soap, tar,\nchocolate, clay",
"No – cast it (plaster of Paris)\nor photograph"],
]
tp2 = Table(print_types, colWidths=[3*cm, 5.5*cm, 4*cm, 3.5*cm])
tp2.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TEAL),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, LIGHT_TEAL]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#80CBC4")),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(tp2)
story.append(Spacer(1, 10))
story.append(Paragraph("Development Methods for Latent Prints", h2_style))
dev_data = [
["Method", "Reagent/Material", "Used On", "Mechanism"],
["Dusting\n(Physical)","Aluminium powder (light)\nCharcoal/graphite (dark)\nFluorescent powder",
"Non-porous:\nglass, metal, plastic","Powder adheres to\nfatty/sebaceous residue"],
["Iodine Fuming\n(Chemical)","Iodine crystals\n(heated/sublimed)",
"Paper, cardboard","Temporary – iodine binds\nto fatty acids; fades"],
["Silver Nitrate\n(Chemical)","5% AgNO₃ solution +\nsunlight/UV",
"Porous: paper,\nwood","Reacts with NaCl in\nsweat → AgCl (dark)"],
["Ninhydrin\n(Chemical)","0.6% ninhydrin in\nacetone/ethanol",
"Porous: paper,\ndocuments","Reacts with amino acids\nin sweat; purple colour\n(Ruhemann's purple)"],
["Cyanoacrylate\nFuming","Super-glue fumes\n(ethyl cyanoacrylate)",
"Non-porous:\nplastic, rubber","Polymerises on sweat\nresidues; white deposits"],
["Laser/UV\nFluorescence","UV/alternate\nlight source (ALS)",
"Multi-surface","Sweat fluoresces;\nviewed with goggles"],
]
td = Table(dev_data, colWidths=[3.2*cm, 3.5*cm, 3.5*cm, 5.8*cm])
td.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#90A4AE")),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(td)
story.append(Spacer(1, 6))
story.append(ColorBox(
"★ <b>Exam Tip</b>: <b>Ninhydrin</b> = reacts with amino acids (most sensitive for old latent prints on paper). "
"<b>Silver nitrate</b> = reacts with chlorides in sweat. <b>Iodine fuming</b> = temporary (fades). "
"<b>Cyanoacrylate</b> = best for non-porous surfaces.",
bg=LIGHT_GOLD, border=GOLD, style=imp_style
))
story.append(PageBreak())
# ─── PAGE 7: MEDICOLEGAL IMPORTANCE ─────────────────────────────────────
story.append(SectionHeader("6. MEDICOLEGAL IMPORTANCE OF FINGERPRINTS", bg=DARK_BLUE))
story.append(Spacer(1, 8))
ml_points = [
("1. Criminal Identification", DARK_BLUE,
"Fingerprints found at crime scenes (latent/visible) are compared with suspect's prints. "
"A match with >16 matching characteristics (in India, 8-10 in UK) constitutes proof of identity."),
("2. Unknown Dead Body Identification", TEAL,
"Fingerprints of deceased are taken and compared with ante-mortem prints in records. "
"Critical in mass disasters (fires, floods, aircraft crashes)."),
("3. Decomposed / Mutilated Bodies", ORANGE,
"Even in advanced decomposition, ridge patterns may survive. Fingers may be rehydrated "
"with chemicals to obtain prints."),
("4. Legal Documents & Records", PURPLE,
"Used on bank documents, passports, Aadhaar card (UIDAI), voter ID, SIM card registration, "
"and pension verification."),
("5. Paternity / Maternity Disputes", TEAL,
"Ridge patterns show hereditary similarity. Used as supportive (not conclusive) evidence "
"in paternity disputes (DNA profiling is superior)."),
("6. Establishing Age from Prints", DARK_BLUE,
"Fingerprint patterns do not change with age - confirms identity across decades. "
"Useful when other age estimation methods are unavailable."),
("7. Disaster Victim Identification (DVI)", RED,
"International DVI teams use fingerprints as primary identification method in mass disasters "
"(Interpol DVI guidelines: fingerprints are Category 1 primary identifier)."),
("8. Exclusion of Suspects", ORANGE,
"If fingerprints at crime scene do NOT match suspect, it helps exclude the person "
"(exculpatory evidence)."),
]
for title, col, desc in ml_points:
story.append(ColorBox(f"<b>{title}</b>: {desc}", bg=LIGHT_GREY, border=col, style=body_style))
story.append(Spacer(1, 3))
story.append(Spacer(1, 6))
# Comparison: Fingerprints vs DNA
story.append(Paragraph("Fingerprints vs DNA Profiling - Comparison", h2_style))
comp_data = [
["Feature", "Fingerprints", "DNA Profiling"],
["Uniqueness", "Unique (even twins differ)", "Identical twins have same DNA"],
["Permanence", "Permanent (dermis intact)", "Permanent (in cells)"],
["Reliability", "Very high (99%+)", "Near absolute (99.9999%)"],
["Time taken", "Minutes to hours", "Days to weeks"],
["Cost", "Low", "High"],
["Sample needed", "Surface impression", "Blood, hair, saliva, semen, bone"],
["Best used for", "Crime scene identification", "Paternity, missing persons, severe cases"],
["Legal acceptance", "Fully accepted", "Fully accepted (higher evidentiary value)"],
["Limitation", "Burns/acid destroy ridges", "Requires cellular material; costly"],
]
tcomp = Table(comp_data, colWidths=[4*cm, 6.5*cm, 5.5*cm])
tcomp.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#90A4AE")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(tcomp)
story.append(PageBreak())
# ─── PAGE 8: POROSCOPY + SPECIAL TOPICS ──────────────────────────────────
story.append(SectionHeader("7. POROSCOPY, DERMATOGLYPHICS & SPECIAL TOPICS", bg=PURPLE))
story.append(Spacer(1, 8))
story.append(Paragraph("Poroscopy (Locard's Method)", h2_style))
story.append(ColorBox(
"<b>Poroscopy</b> is the study of the <b>shape, size, and arrangement of sweat pores</b> "
"on fingerprint ridges. Introduced by <b>Edmund Locard</b> (French criminologist). "
"Each sweat pore has a unique pattern - their number, position, and shape are permanent. "
"Minimum <b>20-22 pores</b> needed for positive identification. "
"Used when fingerprint ridges are fragmentary or partially visible.",
bg=LIGHT_PURPLE, border=PURPLE
))
story.append(Spacer(1, 8))
story.append(Paragraph("Dermatoglyphics in Clinical Conditions", h2_style))
clin_data = [
["Condition", "Karyotype", "Characteristic Dermatoglyphic Findings"],
["Down Syndrome\n(Trisomy 21)", "47XX/XY, +21",
"• Single palmar crease (Simian crease) in 50%\n• 10 ulnar loops on all digits\n"
"• ATD angle > 57° (normally 40-50°)\n• Increased total ridge count"],
["Klinefelter Syndrome","47XXY",
"• Decreased total ridge count\n• Increased arches\n• Small finger ridge count reduced"],
["Turner Syndrome", "45XO",
"• Increased total ridge count\n• Increased whorls\n• Large thenar pattern"],
["Patau Syndrome\n(Trisomy 13)","47XX/XY, +13",
"• Arches on most fingers\n• Axial triradius at t' or t''\n• Tibial arch on hallucal area of sole"],
["Edward Syndrome\n(Trisomy 18)","47XX/XY, +18",
"• Arches on most or all fingers (very characteristic)\n• Increased frequency of arches"],
]
tc2 = Table(clin_data, colWidths=[3.5*cm, 2.5*cm, 10*cm])
tc2.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), PURPLE),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, LIGHT_PURPLE]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#CE93D8")),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(tc2)
story.append(Spacer(1, 8))
story.append(Paragraph("ATD Angle", h3_style))
story.append(Paragraph(
"The <b>ATD angle</b> is measured between the triradii of the index finger (A), "
"little finger (T), and the axial triradius (D) on the palm. "
"Normal: <b>40-50°</b>. In Down syndrome: <b>>57°</b> due to distal placement "
"of the axial triradius.",
body_style
))
story.append(Spacer(1, 8))
story.append(Paragraph("Obliteration of Fingerprints", h2_style))
story.append(ColorBox(
"<b>Criminals may attempt to obliterate fingerprints</b> by:\n"
"(1) Burning/cauterizing fingertips (2) Cutting with sharp instruments "
"(3) Applying strong acids (4) Sanding/filing the ridges\n\n"
"<b>Important</b>: If only the epidermis is damaged, ridges regenerate from the basal layer. "
"Only destruction of the <b>entire dermis</b> permanently destroys fingerprint ridges. "
"Even after obliteration attempts, the outline may be visible under UV light.",
bg=LIGHT_ORANGE, border=ORANGE, style=body_style
))
story.append(PageBreak())
# ─── PAGE 9: EXAM RAPID REVISION ─────────────────────────────────────────
story.append(SectionHeader("8. RAPID REVISION — HIGH-YIELD EXAM POINTS", bg=RED))
story.append(Spacer(1, 8))
story.append(Paragraph("One-Liners for MCQ / Short Answer", h2_style))
oneliners = [
("First use of fingerprints in India", "Sir William Herschel, 1858, West Bengal"),
("Systematized fingerprint system", "Sir Francis Galton, 1892"),
("First Fingerprint Bureau in world", "Kolkata, India, 1897"),
("Classification system named after", "Galton-Henry System"),
("Frequency of loops", "60-70% (most common)"),
("Frequency of whorls", "25-35%"),
("Frequency of arches", "6-7%"),
("Frequency of composites", "1-2% (rarest)"),
("Delta points: Arches", "0 (no delta)"),
("Delta points: Loops", "1 delta"),
("Delta points: Whorls", "2 deltas"),
("Fingerprint ridge formation starts", "12-16 weeks intrauterine life"),
("Fingerprint formation completed", "24 weeks intrauterine life"),
("Study of sweat pores", "Poroscopy (Locard)"),
("Study of ridge patterns", "Dermatoglyphics"),
("Single palmar crease in", "Down syndrome (Simian crease)"),
("ATD angle in Down syndrome", "> 57° (normal 40-50°)"),
("Increased whorls + high ridge count in", "Turner syndrome (45XO)"),
("Decreased ridge count in", "Klinefelter syndrome (47XXY)"),
("Most reliable identification method", "DNA profiling (99.9999%)"),
("Min characteristics to match (India)", "16 (UK: 8-10)"),
("Ninhydrin reacts with", "Amino acids in sweat (paper prints)"),
("Silver nitrate reacts with", "Chlorides (NaCl) in sweat"),
("Iodine fuming result", "Temporary (fades on exposure)"),
("Best method for non-porous surfaces", "Cyanoacrylate fuming (superglue)"),
("Interpol DVI - fingerprints category", "Category 1 (Primary identifier)"),
("Minimum pores for poroscopy ID", "20-22 pores"),
]
ol_data = [["Question / Key Point", "Answer"]] + oneliners
tol = Table(ol_data, colWidths=[9*cm, 7*cm])
tol.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), RED),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('FONTNAME', (1,1), (1,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (1,1), (1,-1), DARK_BLUE),
('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, colors.HexColor("#FFEBEE")]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#EF9A9A")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 8),
]))
story.append(tol)
story.append(Spacer(1, 8))
story.append(ColorBox(
"<b>MNEMONIC SUMMARY</b>\n\n"
"• Pattern frequency: <b>LWAC</b> — Loops (60-70%), Whorls (25-35%), Arches (6-7%), Composites (1-2%)\n"
"• Properties: <b>PUPE</b> — Permanence, Uniqueness, Practicability, Exactness\n"
"• Deltas: 0 (Arch) → 1 (Loop) → 2 (Whorl/Composite)\n"
"• Development methods: <b>DISC-N</b> — Dusting, Iodine, Silver nitrate, Cyanoacrylate, Ninhydrin\n"
"• Dermatoglyphics in syndromes: Down=Simian crease+ulnar loops, Turner=whorls, Klinefelter=arches",
bg=LIGHT_GREEN, border=GREEN, style=bold_style
))
story.append(Spacer(1, 8))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor("#90A4AE")))
story.append(Spacer(1, 4))
story.append(Paragraph(
"<i>Sources: The Essentials of Forensic Medicine & Toxicology 36th Ed. (Pillay) | "
"P.C. Dikshit Textbook of Forensic Medicine & Toxicology | "
"Parikh's Textbook of Medical Jurisprudence, Forensic Medicine & Toxicology</i>",
caption_style
))
# Build
doc.build(story)
print(f"PDF generated: {OUTPUT}")
if __name__ == "__main__":
build_pdf()
cd /tmp/workspace/fingerprint-study-guide && python generate_pdf.py 2>&1
~/fingerprint-study-guide/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/fingerprint-study-guide/generate_pdf.py'
cd /tmp/workspace/fingerprint-study-guide && python generate_pdf.py 2>&1
ls -lh /tmp/workspace/fingerprint-study-guide/
Fingerprints Dactylography Study Guide
PDF Document · PDF