mkdir -p /tmp/workspace/organic-chem-pdf && cat > /tmp/workspace/organic-chem-pdf/generate_pdf.py << 'PYEOF'
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table,
TableStyle, HRFlowable, PageBreak, KeepTogether)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.graphics.shapes import Drawing, Rect, Line, String, Arrow, Polygon
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import Drawing, Rect, Line, String
from reportlab.platypus import Image as RLImage
import io
# ── Colours ──────────────────────────────────────────────────────────────────
C_NAVY = colors.HexColor('#1A237E')
C_BLUE = colors.HexColor('#1565C0')
C_LTBLUE = colors.HexColor('#E3F2FD')
C_TEAL = colors.HexColor('#00695C')
C_LTTEAL = colors.HexColor('#E0F2F1')
C_ORANGE = colors.HexColor('#E65100')
C_LTORANGE = colors.HexColor('#FFF3E0')
C_RED = colors.HexColor('#B71C1C')
C_LTRED = colors.HexColor('#FFEBEE')
C_PURPLE = colors.HexColor('#4A148C')
C_LTPURPLE = colors.HexColor('#F3E5F5')
C_GREEN = colors.HexColor('#2E7D32')
C_LTGREEN = colors.HexColor('#E8F5E9')
C_GREY = colors.HexColor('#455A64')
C_LTGREY = colors.HexColor('#ECEFF1')
C_YELLOW = colors.HexColor('#F9A825')
C_LTYELLOW = colors.HexColor('#FFFDE7')
C_WHITE = colors.white
C_BLACK = colors.black
PAGE_W, PAGE_H = A4
# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent='Normal', **kwargs):
return ParagraphStyle(name, parent=styles[parent], **kwargs)
title_style = make_style('MyTitle', fontSize=22, textColor=C_WHITE,
alignment=TA_CENTER, fontName='Helvetica-Bold',
spaceAfter=6)
h1_style = make_style('MyH1', fontSize=14, textColor=C_WHITE,
fontName='Helvetica-Bold', spaceBefore=6, spaceAfter=4)
h2_style = make_style('MyH2', fontSize=11, textColor=C_NAVY,
fontName='Helvetica-Bold', spaceBefore=8, spaceAfter=3)
h3_style = make_style('MyH3', fontSize=10, textColor=C_TEAL,
fontName='Helvetica-Bold', spaceBefore=5, spaceAfter=2)
body_style = make_style('MyBody', fontSize=8.5, leading=13,
fontName='Helvetica', spaceAfter=3)
small_style = make_style('MySmall', fontSize=7.5, leading=11,
fontName='Helvetica', spaceAfter=2)
code_style = make_style('MyCode', fontSize=8, fontName='Courier',
backColor=C_LTGREY, leading=12,
leftIndent=6, rightIndent=6, spaceAfter=4)
caption_style = make_style('MyCaption', fontSize=7.5, textColor=C_GREY,
fontName='Helvetica-Oblique', alignment=TA_CENTER)
bullet_style = make_style('MyBullet', fontSize=8.5, leading=12,
fontName='Helvetica', leftIndent=12, bulletIndent=4,
spaceAfter=2, bulletText='\u2022')
center_style = make_style('MyCenter', fontSize=8.5, leading=12,
fontName='Helvetica', alignment=TA_CENTER, spaceAfter=3)
# ── Helpers ───────────────────────────────────────────────────────────────────
def section_header(text, bg=C_NAVY):
"""Full-width coloured header bar."""
data = [[Paragraph(text, h1_style)]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 8),
]))
return t
def subsection_header(text, bg=C_LTBLUE, fg=C_NAVY):
p = ParagraphStyle('sub_hdr', fontSize=10, textColor=fg,
fontName='Helvetica-Bold', alignment=TA_LEFT,
spaceBefore=4, spaceAfter=2)
data = [[Paragraph(text, p)]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('LEFTPADDING', (0,0), (-1,-1), 8),
('BOX', (0,0), (-1,-1), 0.5, fg),
]))
return t
def reaction_box(text, bg=C_LTYELLOW, border=C_YELLOW):
"""Monospace reaction box."""
lines = [Paragraph(line.replace(' ', ' '), code_style) for line in text.strip().split('\n')]
data = [[line] for line in lines]
t = Table(data, colWidths=[16.6*cm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('BOX', (0,0), (-1,-1), 1, border),
('TOPPADDING', (0,0), (-1,-1), 2),
('BOTTOMPADDING', (0,0), (-1,-1), 2),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
]))
return t
def key_point(text, color=C_LTGREEN, border=C_GREEN):
p = Paragraph('<b>KEY:</b> ' + text, body_style)
data = [[p]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), color),
('BOX', (0,0), (-1,-1), 0.8, border),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 8),
]))
return t
def make_table(headers, rows, col_widths=None, header_bg=C_NAVY, row_alt=C_LTBLUE):
if col_widths is None:
col_widths = [17*cm / len(headers)] * len(headers)
hdr_style = ParagraphStyle('th', fontSize=8, fontName='Helvetica-Bold',
textColor=C_WHITE, alignment=TA_CENTER)
cell_style = ParagraphStyle('td', fontSize=7.5, fontName='Helvetica',
textColor=C_BLACK, leading=11)
data = [[Paragraph(h, hdr_style) for h in headers]]
for row in rows:
data.append([Paragraph(str(c), cell_style) for c in row])
t = Table(data, colWidths=col_widths, repeatRows=1)
style_cmds = [
('BACKGROUND', (0,0), (-1,0), header_bg),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('LEFTPADDING', (0,0), (-1,-1), 4),
('RIGHTPADDING', (0,0), (-1,-1), 4),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor('#90A4AE')),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]
for i in range(1, len(data)):
if i % 2 == 0:
style_cmds.append(('BACKGROUND', (0,i), (-1,i), row_alt))
t.setStyle(TableStyle(style_cmds))
return t
def flowchart_box(text, bg, fg=C_WHITE, w=4*cm, h=1.2*cm):
"""Returns a small coloured box paragraph for flowchart cells."""
p = ParagraphStyle('fc', fontSize=8, fontName='Helvetica-Bold',
textColor=fg, alignment=TA_CENTER, leading=10)
data = [[Paragraph(text, p)]]
t = Table(data, colWidths=[w], rowHeights=[h])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('BOX', (0,0), (-1,-1), 1, colors.HexColor('#37474F')),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))
return t
def arrow_row():
return Paragraph('<font color="#546E7A"><b>▼</b></font>', center_style)
# ── Cover ─────────────────────────────────────────────────────────────────────
def build_cover():
elems = []
# Big title block
cover_bg_data = [[
Paragraph('<font color="white"><b>ORGANIC CHEMISTRY</b></font>',
ParagraphStyle('ct', fontSize=28, fontName='Helvetica-Bold',
textColor=C_WHITE, alignment=TA_CENTER)),
]]
elems.append(Spacer(1, 2*cm))
ct = Table([[Paragraph(
'<font color="white"><b>ORGANIC CHEMISTRY</b><br/>'
'<font size="16">Quick Reference Guide</font><br/>'
'<font size="11">B.Sc / B.Pharm Level</font></font>',
ParagraphStyle('cov', fontSize=26, fontName='Helvetica-Bold',
textColor=C_WHITE, alignment=TA_CENTER, leading=34)
)]], colWidths=[17*cm])
ct.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), C_NAVY),
('TOPPADDING', (0,0), (-1,-1), 24),
('BOTTOMPADDING', (0,0), (-1,-1), 24),
('BOX', (0,0), (-1,-1), 3, C_BLUE),
]))
elems.append(ct)
elems.append(Spacer(1, 0.5*cm))
topics = [
('Elimination', 'E1 & E2 Mechanisms'),
('Substitution', 'SN1 & SN2 Mechanisms'),
('Addition', 'Markovnikov & Anti-Markovnikov'),
('Alkyl Halides', 'Structure & Uses'),
('Alcohols', 'Qualitative Tests, Structure & Uses'),
('Condensations', 'Aldol, Cannizzaro, Benzoin, Perkin'),
('Carbonyl', 'Qualitative Tests, Structure & Uses'),
('Carboxylic Acids', 'Preparation, Tests & Uses'),
('Amines', 'Preparation, Tests & Uses'),
]
p_style = ParagraphStyle('tp', fontSize=9, fontName='Helvetica',
textColor=C_NAVY, leading=12)
cat_style = ParagraphStyle('cat', fontSize=8, fontName='Helvetica-Bold',
textColor=C_TEAL)
rows = [[Paragraph(f'<b>{c}</b>', cat_style), Paragraph(d, p_style)]
for c, d in topics]
tbl = Table(rows, colWidths=[4*cm, 12*cm])
tbl.setStyle(TableStyle([
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 8),
('LINEBELOW', (0,0), (-1,-2), 0.3, colors.HexColor('#CFD8DC')),
('BACKGROUND', (0,0), (-1,-1), C_LTBLUE),
('BOX', (0,0), (-1,-1), 0.5, C_BLUE),
]))
elems.append(tbl)
elems.append(Spacer(1, 0.4*cm))
elems.append(Paragraph('Reactions • Mechanisms • Tests • Uses',
ParagraphStyle('sub', fontSize=10, textColor=C_GREY,
fontName='Helvetica-Oblique',
alignment=TA_CENTER)))
elems.append(PageBreak())
return elems
# ── Section 1: E1 & E2 ────────────────────────────────────────────────────────
def build_e1_e2():
elems = []
elems.append(section_header('1. ELIMINATION REACTIONS: E1 & E2'))
elems.append(Spacer(1, 3*mm))
# E1
elems.append(subsection_header('E1 — Unimolecular Elimination', C_LTBLUE, C_NAVY))
elems.append(reaction_box(
'Step 1 (RDS): R-X ──slow──> R⁺ + X⁻ [carbocation formed]\n'
'Step 2 (fast): Base + R⁺ ──> Alkene + BH⁺'
))
elems.append(make_table(
['Feature', 'E1 Detail'],
[['Rate law', 'k[R-X] (1st order, unimolecular)'],
['Favored substrate', '3° >> 2° (stable carbocation)'],
['Base', 'Weak base (H₂O, ROH)'],
['Solvent', 'Polar protic (EtOH, H₂O)'],
['Intermediate', 'Carbocation (planar)'],
['Rearrangement', 'Yes (hydride/methyl shift possible)'],
['Stereochemistry', 'Non-specific → Zaitsev product (more substituted alkene)']],
[5*cm, 12*cm]
))
elems.append(Spacer(1, 3*mm))
# E2
elems.append(subsection_header('E2 — Bimolecular Elimination (Concerted)', C_LTTEAL, C_TEAL))
elems.append(reaction_box(
'One concerted step:\n'
'Base–H---Cβ–Cα–LG (anti-periplanar, 180°)\n'
'Base removes β-H SIMULTANEOUSLY as LG departs → Alkene'
))
elems.append(make_table(
['Feature', 'E2 Detail'],
[['Rate law', 'k[R-X][Base] (2nd order)'],
['Favored substrate', '3° > 2° > 1°'],
['Base', 'Strong, bulky (KOH, NaOEt, t-BuOK)'],
['Solvent', 'Polar aprotic (DMSO, acetone)'],
['Intermediate', 'None (transition state only)'],
['Rearrangement', 'No'],
['Stereo', 'ANTI-periplanar geometry required → TRANS alkene preferred'],
['Regioselectivity', 'KOH/EtOH → Zaitsev (more sub.); t-BuOK → Hofmann (less sub.)']],
[5*cm, 12*cm]
))
elems.append(Spacer(1, 3*mm))
# Comparison
elems.append(subsection_header('E1 vs E2 Comparison', C_LTRED, C_RED))
elems.append(make_table(
['', 'E1', 'E2'],
[['Steps', '2 (stepwise)', '1 (concerted)'],
['Order', '1st', '2nd'],
['Base', 'Weak', 'Strong'],
['Solvent', 'Polar protic', 'Polar aprotic'],
['Intermediate', 'Carbocation', 'None'],
['Rearrangement', '✓ Yes', '✗ No'],
['Stereospecific', '✗ No', '✓ Yes (anti)']],
[3*cm, 7*cm, 7*cm],
header_bg=C_RED
))
elems.append(PageBreak())
return elems
# ── Section 2: SN1 & SN2 ─────────────────────────────────────────────────────
def build_sn1_sn2():
elems = []
elems.append(section_header('2. NUCLEOPHILIC SUBSTITUTION: SN1 & SN2'))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('SN1 — Unimolecular Nucleophilic Substitution', C_LTBLUE, C_NAVY))
elems.append(reaction_box(
'Step 1 (RDS): R-X ──> R⁺ + X⁻\n'
'Step 2: Nu: + R⁺ ──> R-Nu\n'
' (attack from BOTH faces → RACEMIZATION)'
))
elems.append(make_table(
['Feature', 'SN1 Detail'],
[['Rate', 'k[R-X] — 1st order'],
['Substrate', '3° >> 2° (stable carbocation)'],
['Nucleophile', 'Weak / neutral (H₂O, ROH)'],
['Solvent', 'Polar protic'],
['Stereochemistry', 'Racemization (±) at chiral centre'],
['Rearrangement', 'Yes']],
[5*cm, 12*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('SN2 — Bimolecular Nucleophilic Substitution (Backside Attack)', C_LTTEAL, C_TEAL))
elems.append(reaction_box(
'Nu: ------> C ...... LG (180° backside attack, concerted)\n'
'Nu: + R-X ──> Nu-R + X⁻\n'
'INVERSION of configuration (Walden inversion)'
))
elems.append(make_table(
['Feature', 'SN2 Detail'],
[['Rate', 'k[R-X][Nu] — 2nd order'],
['Substrate', '1° >> 2° (3° = NO reaction, steric block)'],
['Nucleophile', 'Strong (I⁻, CN⁻, RS⁻, OH⁻, NH₃)'],
['Solvent', 'Polar aprotic (DMSO, DMF, acetone)'],
['Stereochemistry', 'Inversion of configuration'],
['Rearrangement', 'No']],
[5*cm, 12*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('SN1 vs SN2 Quick Comparison', C_LTRED, C_RED))
elems.append(make_table(
['', 'SN1', 'SN2'],
[['Order', '1st', '2nd'],
['Substrate', '3° >> 2°', '1° >> 2° (3° blocked)'],
['Nucleophile', 'Weak', 'Strong'],
['Solvent', 'Polar protic', 'Polar aprotic'],
['Intermediate', 'Carbocation', 'None'],
['Stereo', 'Racemization', 'Inversion'],
['Rearrangement', '✓ Yes', '✗ No']],
[4*cm, 6.5*cm, 6.5*cm],
header_bg=C_RED
))
elems.append(PageBreak())
return elems
# ── Section 3: Markovnikov ────────────────────────────────────────────────────
def build_markovnikov():
elems = []
elems.append(section_header('3. MARKOVNIKOV & ANTI-MARKOVNIKOV ADDITION', bg=C_TEAL))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Markovnikov Addition (HX to Alkene)', C_LTBLUE, C_NAVY))
elems.append(Paragraph(
'<b>Rule:</b> H adds to carbon with <b>more H atoms</b> (less substituted); '
'X adds to carbon with <b>fewer H atoms</b> (more substituted). '
'Basis: more stable carbocation intermediate.', body_style))
elems.append(reaction_box(
'CH₃-CH=CH₂ + HBr ──> CH₃-CHBr-CH₃ (Markovnikov, MAJOR)\n'
' NOT CH₃-CH₂-CH₂Br'
))
elems.append(Spacer(1, 2*mm))
elems.append(subsection_header('Anti-Markovnikov — Hydroboration-Oxidation', C_LTTEAL, C_TEAL))
elems.append(reaction_box(
'Alkene + BH₃·THF ──> Organoborane (syn addition)\n'
' + H₂O₂/NaOH ──> Anti-Markovnikov ALCOHOL\n\n'
'CH₃-CH=CH₂ ──(1)BH₃ (2)H₂O₂/OH⁻──> CH₃-CH₂-CH₂OH (1-propanol)\n'
'Stereochemistry: SYN addition → cis product'
))
elems.append(Spacer(1, 2*mm))
elems.append(subsection_header('Anti-Markovnikov — Free Radical HBr (Peroxide/hν)', C_LTORANGE, C_ORANGE))
elems.append(reaction_box(
'Alkene + HBr + ROOR (or hν) ──> Anti-Markovnikov alkyl bromide\n\n'
'CH₃-CH=CH₂ + HBr/ROOR ──> CH₃-CH₂-CH₂Br (1-bromopropane)\n\n'
'Mechanism: Radical chain\n'
' Initiation: ROOR ──> 2 RO•; RO• + HBr ──> Br•\n'
' Propagation: Br• + alkene ──> more stable radical ──> + HBr ──> product\n\n'
'NOTE: Only HBr shows anti-Markovnikov radical addition.\n'
' HCl and HI do NOT (bond energies unfavorable).'
))
elems.append(Spacer(1, 2*mm))
elems.append(make_table(
['Addition Type', 'Reagent', 'Product', 'Stereochem'],
[['Markovnikov', 'HX (no peroxide)', 'More sub. X-product', 'Mixture'],
['Anti-Markovnikov', 'HBr + ROOR / hν', 'Less sub. Br-product', 'Mixture'],
['Hydroboration-Ox.', 'BH₃; H₂O₂/OH⁻', 'Less sub. Alcohol', 'Syn addition'],
['Acid hydration', 'H₂O / H₂SO₄', 'More sub. Alcohol (Markov.)', 'Mixture']],
[4.5*cm, 4.5*cm, 5.5*cm, 2.5*cm]
))
elems.append(PageBreak())
return elems
# ── Section 4: Alkyl Halides ──────────────────────────────────────────────────
def build_alkyl_halides():
elems = []
elems.append(section_header('4. ALKYL HALIDES — STRUCTURE & USES', bg=C_PURPLE))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Structure & Classification', C_LTPURPLE, C_PURPLE))
elems.append(make_table(
['Type', 'Formula', 'Example', 'Reactivity'],
[['Primary (1°)', 'R-CH₂-X', 'CH₃CH₂Cl', 'SN2 preferred'],
['Secondary (2°)', 'R₂CH-X', '(CH₃)₂CHBr', 'SN1 or SN2'],
['Tertiary (3°)', 'R₃C-X', '(CH₃)₃CBr', 'SN1/E1 preferred'],
['Allyl/Benzyl', 'CH₂=CH-CH₂X', 'Allyl chloride', 'Highly reactive (SN1/SN2)'],
['Vinyl/Aryl', 'CH₂=CH-X', 'Vinyl chloride', 'Very unreactive (no SN)']],
[3*cm, 4*cm, 4*cm, 6*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Bond Properties', C_LTBLUE, C_NAVY))
elems.append(make_table(
['Bond', 'Bond Length', 'Bond Energy', 'Polarity'],
[['C-F', 'Shortest', 'Strongest', 'Most polar'],
['C-Cl', '↑', '↑', '↑'],
['C-Br', '↑', '↑', '↑'],
['C-I', 'Longest', 'Weakest', 'Least polar']],
[4*cm, 4*cm, 5*cm, 4*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Important Alkyl Halides & Uses', C_LTTEAL, C_TEAL))
elems.append(make_table(
['Compound', 'Formula', 'Uses'],
[['Chloromethane', 'CH₃Cl', 'Methylating agent; silicone polymer synthesis'],
['Dichloromethane', 'CH₂Cl₂', 'Solvent (paint remover, pharmaceutical extractions)'],
['Chloroform', 'CHCl₃', 'Solvent; once used as anesthetic'],
['Carbon tetrachloride', 'CCl₄', 'Solvent; fire extinguisher (obsolete)'],
['Iodoform', 'CHI₃', 'Antiseptic; iodoform test reagent'],
['Freons (CFCs)', 'CCl₂F₂', 'Refrigerants (restricted - ozone depletion)'],
['Halothane', 'CF₃CHBrCl', 'General anesthetic (inhalation)'],
['Chlorobutanol', '(CH₃)₂C(OH)CH₂Cl₃', 'Preservative in injectable preparations'],
['DDT', 'ClC₆H₄-CHCCl₃', 'Insecticide (now banned - persistent organic pollutant)'],
['Methyl bromide', 'CH₃Br', 'Agricultural fumigant; methylating agent']],
[4*cm, 4*cm, 9*cm]
))
elems.append(PageBreak())
return elems
# ── Section 5: Alcohols ───────────────────────────────────────────────────────
def build_alcohols():
elems = []
elems.append(section_header('5. ALCOHOLS — QUALITATIVE TESTS, STRUCTURE & USES', bg=C_GREEN))
elems.append(Spacer(1, 3*mm))
# Flowchart for alcohol ID
elems.append(subsection_header('FLOWCHART: Identification of Alcohols (1°, 2°, 3°)', C_LTGREEN, C_GREEN))
fc_style = ParagraphStyle('fc2', fontSize=8, fontName='Helvetica', leading=11, alignment=TA_CENTER)
yes_no_style = ParagraphStyle('yn', fontSize=7.5, fontName='Helvetica-Bold',
textColor=C_GREEN, alignment=TA_CENTER)
fc_data = [
[Paragraph('<b>Unknown compound</b>\n(C ≤ 6)', fc_style)],
[Paragraph('▼', fc_style)],
[Paragraph('<b>Lucas Test</b>\n(ZnCl₂ + conc. HCl, room temp)', fc_style)],
[Paragraph('▼', fc_style)],
]
lucas_result_data = [
[Paragraph('<b>Immediate turbidity</b>\n(within 1 min)', fc_style),
Paragraph('<b>Turbidity in 5 min</b>\n(or on warming)', fc_style),
Paragraph('<b>No turbidity</b>\n(even on heating)', fc_style)],
[Paragraph('▼', fc_style), Paragraph('▼', fc_style), Paragraph('▼', fc_style)],
[Paragraph('<font color="white"><b>TERTIARY</b></font>',
ParagraphStyle('r', fontSize=9, fontName='Helvetica-Bold', textColor=C_WHITE, alignment=TA_CENTER)),
Paragraph('<font color="white"><b>SECONDARY</b></font>',
ParagraphStyle('r', fontSize=9, fontName='Helvetica-Bold', textColor=C_WHITE, alignment=TA_CENTER)),
Paragraph('<font color="white"><b>PRIMARY</b></font>',
ParagraphStyle('r', fontSize=9, fontName='Helvetica-Bold', textColor=C_WHITE, alignment=TA_CENTER))],
]
top_t = Table(fc_data, colWidths=[17*cm])
top_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), C_LTBLUE),
('BACKGROUND', (0,2), (0,2), C_LTBLUE),
('BOX', (0,0), (0,0), 1, C_NAVY),
('BOX', (0,2), (0,2), 1, C_NAVY),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
]))
elems.append(top_t)
result_t = Table(lucas_result_data, colWidths=[5.67*cm, 5.66*cm, 5.67*cm])
result_t.setStyle(TableStyle([
('BACKGROUND', (0,2), (0,2), C_RED),
('BACKGROUND', (1,2), (1,2), C_ORANGE),
('BACKGROUND', (2,2), (2,2), C_TEAL),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('BOX', (0,2), (0,2), 1, C_RED),
('BOX', (1,2), (1,2), 1, C_ORANGE),
('BOX', (2,2), (2,2), 1, C_TEAL),
]))
elems.append(result_t)
elems.append(Spacer(1, 4*mm))
elems.append(subsection_header('Qualitative Tests Summary', C_LTBLUE, C_NAVY))
elems.append(make_table(
['Test', 'Reagent', 'Observation', 'Identifies'],
[['Lucas Test', 'ZnCl₂ + conc. HCl', 'Turbidity (see flowchart)', '1°, 2°, 3°'],
['Victor Meyer', 'P+I₂; AgNO₂; HNO₂+KOH', 'Red=1°; Blue=2°; Colourless=3°', '1°, 2°, 3°'],
['Iodoform', 'I₂ + NaOH (NaOI)', 'Yellow CHI₃ precipitate (antiseptic smell)', 'CH₃CHOH-R; EtOH'],
['CAN Test', 'Ceric ammonium nitrate/HNO₃', 'Red/orange colour', 'All alcohols'],
['K₂Cr₂O₇/H⁺', 'Acidified dichromate', 'Orange→Green: 1°,2° react; 3° no change', '3° vs others'],
['KMnO₄', 'Acidic KMnO₄', '1° & 2° decolorize; 3° no change', '3° vs others']],
[3.2*cm, 4.5*cm, 6*cm, 3.3*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Important Alcohols & Uses', C_LTGREEN, C_GREEN))
elems.append(make_table(
['Alcohol', 'Formula', 'Key Uses'],
[['Methanol', 'CH₃OH', 'Fuel, solvent, antifreeze (TOXIC - causes blindness)'],
['Ethanol', 'C₂H₅OH', 'Beverages, antiseptic (70%), solvent, fuel additive'],
['Isopropanol', '(CH₃)₂CHOH', 'Rubbing alcohol (70%), antiseptic, solvent'],
['Ethylene glycol', 'HOCH₂CH₂OH', 'Antifreeze, synthesis of polyester (PET)'],
['Glycerol', 'C₃H₅(OH)₃', 'Pharmaceutical excipient, cosmetics, soap making'],
['Benzyl alcohol', 'C₆H₅CH₂OH', 'Preservative in injectable preparations'],
['Cetyl alcohol', 'C₁₆H₃₃OH', 'Emollient in creams and ointments'],
['Benzyl benzoate', 'C₆H₅CH₂OOCC₆H₅', 'Scabicide; treatment of scabies & pediculosis']],
[3.5*cm, 4*cm, 9.5*cm]
))
elems.append(PageBreak())
return elems
# ── Section 6: Condensations ──────────────────────────────────────────────────
def build_condensations():
elems = []
elems.append(section_header('6. CONDENSATION REACTIONS', bg=C_ORANGE))
elems.append(Spacer(1, 3*mm))
# Aldol
elems.append(subsection_header('Aldol Condensation', C_LTYELLOW, C_ORANGE))
elems.append(Paragraph(
'<b>Conditions:</b> Dilute NaOH or dilute HCl; two identical carbonyl compounds with α-H.',
body_style))
elems.append(reaction_box(
'BASE MECHANISM:\n'
'Step 1: Base removes α-H ──> Enolate ion (nucleophile)\n'
'Step 2: Enolate + C=O of another molecule ──> β-hydroxy carbonyl (ALDOL product)\n'
'Step 3: Heat ──> Dehydration ──> α,β-unsaturated carbonyl\n\n'
'Example:\n'
'2 CH₃CHO ──dil.NaOH──> CH₃CH(OH)CH₂CHO (aldol addition)\n'
' ──heat──> CH₃CH=CHCHO + H₂O (crotonaldehyde)'
))
elems.append(Spacer(1, 2*mm))
# Cross-Aldol
elems.append(subsection_header('Cross-Aldol (Claisen-Schmidt)', C_LTYELLOW, C_ORANGE))
elems.append(Paragraph(
'<b>Useful when ONE component has NO α-H</b> (aromatic aldehyde) → single product formed.',
body_style))
elems.append(reaction_box(
'C₆H₅CHO + CH₃COCH₃ ──NaOH──> C₆H₅CH=CHCOCH₃ + H₂O\n'
'(benzaldehyde + acetone ──> benzalacetone) [Claisen-Schmidt]'
))
elems.append(Spacer(1, 3*mm))
# Cannizzaro
elems.append(subsection_header('Cannizzaro Reaction', C_LTBLUE, C_NAVY))
elems.append(Paragraph(
'<b>Conditions:</b> Aldehydes with <b>NO α-H</b> + <b>concentrated KOH/NaOH</b>. '
'Disproportionation via intramolecular hydride transfer.',
body_style))
elems.append(reaction_box(
'Mechanism: OH⁻ + ArCHO ──> Tetrahedral adduct ──(hydride transfer)──> ArCOO⁻ + ArCH₂O⁻\n\n'
'2 HCHO ──conc.KOH──> HCOONa + CH₃OH\n'
'2 C₆H₅CHO ──conc.KOH──> C₆H₅COONa + C₆H₅CH₂OH\n'
' (sodium benzoate) (benzyl alcohol)'
))
elems.append(Spacer(1, 2*mm))
# Cross-Cannizzaro
elems.append(subsection_header('Cross-Cannizzaro Reaction', C_LTBLUE, C_NAVY))
elems.append(Paragraph(
'<b>HCHO is ALWAYS preferentially oxidized</b> (most reactive hydride donor).',
body_style))
elems.append(reaction_box(
'HCHO + C₆H₅CHO ──conc.KOH──> HCOONa + C₆H₅CH₂OH\n'
'(HCHO oxidized ──> formate; ArCHO reduced ──> benzyl alcohol)'
))
elems.append(Spacer(1, 3*mm))
# Benzoin
elems.append(subsection_header('Benzoin Condensation', C_LTPURPLE, C_PURPLE))
elems.append(Paragraph(
'<b>Conditions:</b> Aromatic aldehyde + KCN catalyst (or thiazolium salt / NHC). '
'Umpolung: CN⁻ reverses polarity of carbonyl.',
body_style))
elems.append(reaction_box(
'Mechanism:\n'
'1. CN⁻ + ArCHO ──> Cyanohydrin carbanion (CN⁻ = umpolung catalyst)\n'
'2. Carbanion attacks second ArCHO\n'
'3. CN⁻ eliminated ──> α-hydroxy ketone (BENZOIN)\n\n'
'2 C₆H₅CHO ──KCN, EtOH/H₂O──> C₆H₅-CO-CH(OH)-C₆H₅\n'
' (benzoin)\n'
'Benzoin ──[O]──> C₆H₅-CO-CO-C₆H₅ (benzil)'
))
elems.append(Spacer(1, 3*mm))
# Perkin
elems.append(subsection_header('Perkin Reaction', C_LTRED, C_RED))
elems.append(Paragraph(
'<b>Conditions:</b> Aromatic aldehyde + acid anhydride + sodium/potassium salt of the '
'corresponding acid (weak base) + heat. Gives trans (E) α,β-unsaturated acid.',
body_style))
elems.append(reaction_box(
'Mechanism:\n'
'1. CH₃COO⁻ removes α-H from (CH₃CO)₂O ──> Enolate (acylated carbanion)\n'
'2. Enolate + ArCHO ──> Aldol-type addition\n'
'3. Intramolecular acyl transfer + elimination ──> Anhydride\n'
'4. Hydrolysis ──> α,β-unsaturated acid (trans/E isomer)\n\n'
'C₆H₅CHO + (CH₃CO)₂O ──CH₃COONa, Δ──> C₆H₅CH=CHCOOH + CH₃COOH\n'
' (benzaldehyde + acetic anhydride ──> cinnamic acid) [always E-isomer]'
))
elems.append(PageBreak())
return elems
# ── Section 7: Carbonyl Tests & Uses ─────────────────────────────────────────
def build_carbonyl():
elems = []
elems.append(section_header('7. CARBONYL COMPOUNDS — TESTS, STRUCTURE & USES', bg=C_TEAL))
elems.append(Spacer(1, 3*mm))
# Flowchart for carbonyl tests
elems.append(subsection_header('FLOWCHART: Identification of Carbonyl Compounds', C_LTTEAL, C_TEAL))
fc_p = ParagraphStyle('fc3', fontSize=8, fontName='Helvetica', leading=11, alignment=TA_CENTER)
flow_data = [
['Unknown compound', '', ''],
['▼', '', ''],
['2,4-DNP Test\n(Brady\'s reagent)', '', ''],
['Orange/red ppt ✓', '', 'No ppt ✗\n(no C=O present)'],
['▼', '', ''],
['Tollens\' Test\n[Ag(NH₃)₂]⁺ OH⁻', '', ''],
['Silver mirror ✓\n→ ALDEHYDE', '', 'No silver mirror\n→ KETONE'],
['▼', '', ''],
['Fehling\'s Test\nFehling\'s A + B', '', ''],
['Red Cu₂O ppt ✓\n→ ALIPHATIC\nALDEHYDE', '', 'No red ppt\n→ AROMATIC\nALDEHYDE'],
]
for row in flow_data:
p0 = Paragraph(row[0], fc_p)
bg0 = C_LTBLUE if ('Test' in row[0] or 'Unknown' in row[0]) else (
C_LTGREEN if '✓' in row[0] else (C_LTRED if '✗' in row[0] or 'No' in row[0] else C_WHITE))
cell_data = [[p0]]
ct = Table(cell_data, colWidths=[17*cm])
style_t = [
('BACKGROUND', (0,0), (-1,-1), bg0),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
]
if 'Test' in row[0] or 'Unknown' in row[0]:
style_t.append(('BOX', (0,0), (-1,-1), 1, C_NAVY))
ct.setStyle(TableStyle(style_t))
elems.append(ct)
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Tests Summary Table', C_LTBLUE, C_NAVY))
elems.append(make_table(
['Test', 'Reagent', 'Observation', 'Identifies'],
[['2,4-DNP (Brady\'s)', '2,4-DNP in MeOH/HCl', 'Orange/red crystalline ppt', 'All aldehydes & ketones'],
['Schiff\'s reagent', 'Fuchsin-SO₂ (decolorized)', 'Magenta/pink colour restored', 'Aldehydes only (not ketones)'],
['Tollens\' (silver mirror)', '[Ag(NH₃)₂]⁺ OH⁻', 'Silver mirror / Ag↓ precipitate', 'All aldehydes (not ketones)'],
['Fehling\'s test', 'CuSO₄ + NaOH/tartrate', 'Brick-red Cu₂O precipitate', 'Aliphatic aldehydes; NOT aromatic; NOT ketones'],
['Benedict\'s test', 'CuSO₄ + Na₂CO₃/citrate', 'Brick-red Cu₂O precipitate', 'Reducing sugars & aliphatic aldehydes'],
['Iodoform test', 'I₂ + NaOH', 'Yellow CHI₃ ppt (sweet smell)', 'CH₃CHO; methyl ketones (CH₃COR)']],
[3.5*cm, 4.5*cm, 5*cm, 4*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Important Carbonyl Compounds & Uses', C_LTTEAL, C_TEAL))
elems.append(make_table(
['Compound', 'Formula', 'Key Uses'],
[['Formaldehyde', 'HCHO', 'Formalin (40%) — preservative, disinfectant; Bakelite synthesis; tissue fixative'],
['Acetaldehyde', 'CH₃CHO', 'Acetic acid synthesis; ethanol manufacture'],
['Acetone', '(CH₃)₂CO', 'Solvent (nail polish remover); CHCl₃ synthesis; keto body in DKA'],
['Benzaldehyde', 'C₆H₅CHO', 'Almond flavour; Perkin/Benzoin/Cannizzaro reactions; pharma intermediate'],
['Camphor', 'C₁₀H₁₆O', 'Counter-irritant; topical analgesic; rubefacient in liniments'],
['Cyclohexanone', 'C₆H₁₀O', 'Solvent; caprolactam precursor → nylon-6'],
['Chloral hydrate', 'CCl₃CH(OH)₂', 'Sedative/hypnotic (historically); DDT synthesis'],
['Cinnamaldehyde', 'C₆H₅CH=CHCHO', 'Cinnamon flavour; antimicrobial properties']],
[4*cm, 3.5*cm, 9.5*cm]
))
elems.append(PageBreak())
return elems
# ── Section 8: Carboxylic Acids ───────────────────────────────────────────────
def build_carboxylic():
elems = []
elems.append(section_header('8. CARBOXYLIC ACIDS — PREPARATION, TESTS & USES', bg=C_RED))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Methods of Preparation', C_LTRED, C_RED))
elems.append(make_table(
['Method', 'Reaction / Reagents', 'Note'],
[['Oxidation of 1° alcohol', 'R-CH₂OH + KMnO₄/H⁺ or K₂Cr₂O₇/H₂SO₄ → RCOOH', 'Most common'],
['Oxidation of aldehyde', 'R-CHO + [O] (KMnO₄, Ag₂O, Tollens\') → RCOOH', 'Mild oxidant'],
['Alkene oxidation (vigorous)', 'R-CH=CH₂ + hot KMnO₄ → RCOOH + CO₂', 'C-C bond cleavage'],
['Hydrolysis of nitrile', 'R-CN + H₂O/H⁺ or OH⁻/Δ → RCOOH + NH₃', 'Chain +1 C (via R-X + NaCN)'],
['Ester hydrolysis', 'R-COOR\' + H₂O/H⁺ → RCOOH + R\'OH', 'Acid or base hydrolysis'],
['Grignard + CO₂', 'R-MgX + CO₂; then H₃O⁺ → RCOOH', 'Chain +1 C; dry ether'],
['Hydrolysis of amide', 'R-CONH₂ + H₂O/H⁺ or OH⁻ → RCOOH + NH₃', 'Acidic or basic conditions'],
['Kolbe-Schmitt', 'PhONa + CO₂ (Δ, pressure) → Sodium salicylate → Salicylic acid', 'Aromatic acid'],
['Monsanto process', 'CH₃OH + CO (Rh/I⁻ catalyst) → CH₃COOH', 'Industrial acetic acid']],
[4*cm, 8.5*cm, 4.5*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('FLOWCHART: Qualitative Tests for Carboxylic Acids', C_LTRED, C_RED))
fc_p2 = ParagraphStyle('fc4', fontSize=8, fontName='Helvetica', leading=11, alignment=TA_CENTER)
steps = [
('Test 1: Blue litmus paper', C_LTBLUE, 'Turns RED → compound is acidic'),
('Test 2: NaHCO₃ solution', C_LTBLUE, 'Brisk CO₂ effervescence → CARBOXYLIC ACID\n(Phenols give NO CO₂ with NaHCO₃)'),
('Test 3: Esterification', C_LTBLUE, 'Alcohol + conc. H₂SO₄ + heat → fruity ester smell'),
('Test 4: FeCl₃ (neutral)', C_LTBLUE, 'Buff/flesh precipitate of iron carboxylate\n(Phenols → violet/purple; distinction)'),
]
for step_text, bg, result in steps:
row_data = [
[Paragraph(f'<b>{step_text}</b>', fc_p2),
Paragraph(f'✓ {result}', ParagraphStyle('res', fontSize=8, fontName='Helvetica',
textColor=C_GREEN, alignment=TA_LEFT, leading=11))]
]
rt = Table(row_data, colWidths=[7*cm, 10*cm])
rt.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), bg),
('BACKGROUND', (1,0), (1,0), C_LTGREEN),
('BOX', (0,0), (-1,-1), 0.5, C_RED),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))
elems.append(rt)
elems.append(Spacer(1, 1*mm))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Important Carboxylic Acids & Uses', C_LTRED, C_RED))
elems.append(make_table(
['Acid', 'Formula', 'Key Uses'],
[['Formic acid', 'HCOOH', 'Insect stings (ants); tanning; latex coagulation'],
['Acetic acid', 'CH₃COOH', 'Vinegar (5%); synthesis of aspirin, acetate esters; solvent'],
['Oxalic acid', 'HOOC-COOH', 'Bleaching agent; rust remover; reducing agent'],
['Citric acid', 'C₆H₈O₇', 'Preservative; food flavour; effervescent formulations'],
['Lactic acid', 'CH₃CH(OH)COOH', 'Food preservative; IV fluids; biodegradable plastics'],
['Salicylic acid', '2-HO-C₆H₄-COOH', 'Synthesis of aspirin (ASA); keratolytic agent; antiseptic'],
['Benzoic acid', 'C₆H₅COOH', 'Preservative (Na-benzoate); antiseptic; pharma synthesis'],
['Tartaric acid', 'HOOCCH(OH)CH(OH)COOH', 'Cream of tartar; Fehling\'s B; photography'],
['Stearic acid', 'C₁₇H₃₅COOH', 'Soap; cosmetics; lubricants (solid fat)'],
['Oleic acid', 'C₁₇H₃₃COOH', 'Soft soap; emollient; cooking oil (olive)']],
[3.5*cm, 4.5*cm, 9*cm]
))
elems.append(PageBreak())
return elems
# ── Section 9: Amines ─────────────────────────────────────────────────────────
def build_amines():
elems = []
elems.append(section_header('9. AMINES — PREPARATION, TESTS & USES', bg=C_GREY))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Methods of Preparation', C_LTGREY, C_GREY))
elems.append(make_table(
['Method', 'Reaction', 'Product'],
[['Reduction of NO₂', 'Ar-NO₂ + Fe/HCl or H₂/Pd → Ar-NH₂', '1° Aromatic amine (aniline)'],
['Reduction of nitrile', 'R-CN + LiAlH₄ or H₂/Ni → R-CH₂-NH₂', '1° amine (+1 carbon)'],
['Reduction of amide', 'R-CONH₂ + LiAlH₄ → R-CH₂-NH₂', '1° (same carbon chain)'],
['Reduction of oxime', 'R-CH=N-OH + H₂/Ni → R-CH₂-NH₂', '1° amine'],
['Hofmann Rearrangement', 'R-CONH₂ + Br₂/NaOH → R-NH₂ + CO₂', '1° amine (-1 carbon)'],
['Gabriel synthesis', 'Phthalimide → K-salt → N-alkylation → hydrazine → R-NH₂', 'PURE 1° amine'],
['Reductive amination', 'R-CHO + NH₃ → Imine + H₂/Ni → R-CH₂-NH₂', '1°, 2°, or 3° amine'],
['Ammonolysis of RX', 'R-X + excess NH₃ → 1° + 2° + 3° amines + quat. salt', 'Mixture (low utility)'],
['Schmidt reaction', 'R-COOH + HN₃/H₂SO₄ → R-NH₂ + CO₂ + N₂', '1° amine (-1 carbon)']],
[4.5*cm, 8*cm, 4.5*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('FLOWCHART: Qualitative Tests for Amines', C_LTGREY, C_GREY))
flow_rows = [
('Start: Unknown amine', C_LTBLUE, C_NAVY),
('▼ Turns red litmus BLUE (basic) ▼', C_LTYELLOW, C_ORANGE),
('Hinsberg Test (PhSO₂Cl + KOH)', C_LTBLUE, C_NAVY),
]
for text, bg, border in flow_rows:
ft = Table([[Paragraph(text, ParagraphStyle('fct', fontSize=8.5,
fontName='Helvetica-Bold' if '▼' not in text else 'Helvetica',
alignment=TA_CENTER, leading=12,
textColor=C_NAVY if bg != C_LTBLUE else C_NAVY))]],
colWidths=[17*cm])
ft.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),bg),
('BOX',(0,0),(-1,-1),1,border),
('TOPPADDING',(0,0),(-1,-1),4),
('BOTTOMPADDING',(0,0),(-1,-1),4),
('ALIGN',(0,0),(-1,-1),'CENTER')]))
elems.append(ft)
hinsberg_cols = [
('Dissolves in KOH\nafter ppt forms', 'PRIMARY (1°)\nHas acidic N-H on sulfonamide', C_TEAL),
('Precipitate insoluble\nin KOH', 'SECONDARY (2°)\nNo acidic N-H', C_ORANGE),
('No reaction /\ndissolves directly', 'TERTIARY (3°)\nNo N-H to react', C_PURPLE),
]
h_obs = [Paragraph(o, ParagraphStyle('hobs', fontSize=7.5, fontName='Helvetica',
alignment=TA_CENTER, leading=11)) for o, r, c in hinsberg_cols]
h_res = [Paragraph(r, ParagraphStyle('hres', fontSize=8, fontName='Helvetica-Bold',
textColor=C_WHITE, alignment=TA_CENTER, leading=11)) for o, r, c in hinsberg_cols]
h_t = Table([h_obs, h_res], colWidths=[5.67*cm, 5.66*cm, 5.67*cm])
h_style = [
('BACKGROUND', (0,0), (-1,0), C_LTBLUE),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
]
for i, (o, r, c) in enumerate(hinsberg_cols):
h_style.append(('BACKGROUND', (i,1), (i,1), c))
h_style.append(('BOX', (i,1), (i,1), 1, C_GREY))
h_t.setStyle(TableStyle(h_style))
elems.append(h_t)
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('All Qualitative Tests Summary', C_LTGREY, C_GREY))
elems.append(make_table(
['Test', 'Reagent', 'Observation', 'Identifies'],
[['Litmus', 'Red litmus', 'Turns blue', 'All amines (basic)'],
['Hinsberg Test', 'PhSO₂Cl + KOH', 'See flowchart above', '1° vs 2° vs 3°'],
['Azo Dye Test', 'NaNO₂/HCl (0-5°C) then β-naphthol/NaOH', 'Orange/red azo dye', '1° Aromatic amines only'],
['Carbylamine Test', 'CHCl₃ + alc. KOH', 'Foul-smelling isocyanide', '1° amines only (aliphatic & aromatic)'],
['HNO₂ (NaNO₂/HCl)', 'NaNO₂ + dil. HCl',
'1°-aliphatic: N₂ gas; 1°-aromatic: diazonium; 2°: yellow nitrosamine; 3°: no reaction',
'Distinguishes all classes'],
['Menshutkin', 'R-X (alkyl halide)', 'Quaternary ammonium salt precipitate', '3° amines']],
[3.5*cm, 5*cm, 6*cm, 2.5*cm]
))
elems.append(Spacer(1, 3*mm))
elems.append(subsection_header('Important Amines & Uses', C_LTGREY, C_GREY))
elems.append(make_table(
['Amine/Compound', 'Uses'],
[['Aniline (C₆H₅NH₂)', 'Dye industry (azo dyes, indigo); rubber accelerators; pharmaceuticals'],
['Ethylenediamine (H₂N-CH₂CH₂-NH₂)', 'EDTA synthesis (chelating agent); pharmaceutical intermediates'],
['Procaine / Lidocaine', 'Local anesthetics (amino-ester / amino-amide type)'],
['Adrenaline / Dopamine', 'Catecholamine neurotransmitters; emergency cardiac drugs'],
['Sulfonamides (H₂N-C₆H₄-SO₂NHR)', 'Antibacterial drugs (first synthetic antibiotics)'],
['Benzalkonium chloride (quat. NH⁺)', 'Antiseptic, disinfectant, preservative in eye drops'],
['Dimethyl sulfoxide (DMSO) + amines', 'Drug delivery vehicle; reaction solvent'],
['Pyridine', 'Solvent; pharmaceutical intermediate; crop protection chemicals'],
['Histamine (imidazole amine)', 'Mediator of allergic reactions; gastric acid secretion'],
['Amphetamine / Ephedrine', 'Sympathomimetic amines; bronchodilators']],
[5*cm, 12*cm]
))
elems.append(PageBreak())
return elems
# ── Section 10: Master Comparison Table ──────────────────────────────────────
def build_master_table():
elems = []
elems.append(section_header('10. MASTER REACTION SUMMARY', bg=C_NAVY))
elems.append(Spacer(1, 3*mm))
elems.append(make_table(
['Reaction', 'Conditions', 'Substrate', 'Key Product', 'Key Feature'],
[['E1', 'Weak base, polar protic, heat', '3° >> 2°', 'Alkene (Zaitsev)', 'Carbocation; rearrangement possible'],
['E2', 'Strong base, polar aprotic', '3° > 2° > 1°', 'Alkene (anti)', 'Concerted; anti-periplanar; no rearrang.'],
['SN1', 'Weak Nu, polar protic', '3° >> 2°', 'Substitution product', 'Carbocation; racemization'],
['SN2', 'Strong Nu, polar aprotic', '1° >> 2°', 'Substitution product', 'Concerted; Walden inversion; no rearrang.'],
['Markovnikov', 'HX (no peroxide)', 'Alkene', 'More subst. X-product', 'Stable carbocation intermediate'],
['Anti-Markovnikov', 'HBr + ROOR / BH₃', 'Alkene', 'Less subst. product', 'Radical or hydroboration mechanism'],
['Aldol', 'dil. OH⁻ or H⁺', 'Carbonyl with α-H', 'β-hydroxy carbonyl → α,β-unsat.', 'Enolate nucleophile; α-H required'],
['Cross-Aldol (C-S)', 'NaOH; ArCHO + ketone', 'One no-α-H', 'α,β-Unsaturated carbonyl', 'Claisen-Schmidt; ArCHO has no α-H'],
['Cannizzaro', 'conc. KOH', 'No α-H aldehyde', 'Acid salt + alcohol', 'Hydride transfer; disproportionation'],
['Cross-Cannizzaro', 'conc. KOH; HCHO + ArCHO', 'Both no α-H', 'ArCH₂OH + HCOO⁻', 'HCHO always oxidized'],
['Benzoin', 'KCN (or NHC) catalyst', 'ArCHO', 'α-Hydroxy ketone', 'Umpolung via CN⁻'],
['Perkin', 'Anhydride + salt, Δ', 'ArCHO only', 'Cinnamic acid (E-isomer)', 'Chain extension; aldol-type mechanism'],
['Hofmann rearrangement', 'Br₂ + NaOH', 'Primary amide', '1° Amine (−1 C)', 'Nitrene intermediate; migration'],
['Gabriel synthesis', 'K-phthalimide + RX + N₂H₄', 'Alkyl halide', 'Pure 1° amine', 'No 2° or 3° amine contamination'],
['Kolbe-Schmitt', 'PhONa + CO₂, Δ, press.', 'Sodium phenoxide', 'Salicylic acid', 'Electrophilic aromatic substitution']],
[3*cm, 4*cm, 3*cm, 4*cm, 3*cm]
))
elems.append(Spacer(1, 4*mm))
# Test summary
elems.append(subsection_header('MASTER QUALITATIVE TESTS REFERENCE', C_LTBLUE, C_NAVY))
elems.append(make_table(
['Test', 'Reagent', 'Positive Result', 'Identifies'],
[['Lucas', 'ZnCl₂ + conc. HCl', 'Turbidity: immediate=3°; 5min=2°; none=1°', 'Alcohol type'],
['Victor Meyer', 'P+I₂; AgNO₂; HNO₂+KOH', 'Red=1°; Blue=2°; Colourless=3°', 'Alcohol type'],
['Iodoform', 'I₂ + NaOH', 'Yellow CHI₃ precipitate', 'CH₃CHOH-R; CH₃CHO; CH₃COR'],
['CAN', 'Ceric ammonium nitrate/HNO₃', 'Red/orange complex', 'All alcohols'],
['2,4-DNP (Brady\'s)', '2,4-DNP/HCl/MeOH', 'Orange/red crystalline ppt', 'All C=O (aldehyde & ketone)'],
['Schiff\'s', 'Fuchsin-SO₂', 'Magenta colour', 'Aldehydes (NOT ketones)'],
['Tollens\'', '[Ag(NH₃)₂]⁺ OH⁻', 'Silver mirror', 'All aldehydes (NOT ketones)'],
['Fehling\'s', 'CuSO₄ + NaOH/tartrate', 'Brick-red Cu₂O↓', 'Aliphatic aldehyde (NOT ArCHO, NOT ketone)'],
['NaHCO₃', 'Sodium bicarbonate', 'Brisk CO₂ effervescence', 'Carboxylic acid (NOT phenol)'],
['FeCl₃', 'Neutral FeCl₃', 'Buff/flesh ppt = acid; Violet = phenol', 'Acid vs phenol'],
['Hinsberg', 'PhSO₂Cl + KOH', 'See flowchart: soluble/insoluble/no rxn', 'Amine type 1°/2°/3°'],
['Carbylamine', 'CHCl₃ + alc. KOH', 'Foul isocyanide smell', '1° amines ONLY'],
['Azo dye', 'NaNO₂/HCl then β-naphthol', 'Orange/red azo dye', '1° Aromatic amines ONLY'],
['HNO₂ test', 'NaNO₂ + dil. HCl', 'Gas=1°-aliph; diazonium=1°-arom; yellow oil=2°; none=3°', 'All amine classes']],
[3*cm, 4.5*cm, 5.5*cm, 4*cm]
))
return elems
# ── Footer ────────────────────────────────────────────────────────────────────
def add_footer(canvas, doc):
canvas.saveState()
canvas.setFont('Helvetica', 7)
canvas.setFillColor(C_GREY)
canvas.drawString(2*cm, 1.2*cm, 'Organic Chemistry Quick Reference | B.Sc / B.Pharm Level')
canvas.drawRightString(PAGE_W - 2*cm, 1.2*cm, f'Page {doc.page}')
canvas.setStrokeColor(C_LTBLUE)
canvas.setLineWidth(0.5)
canvas.line(2*cm, 1.5*cm, PAGE_W - 2*cm, 1.5*cm)
canvas.restoreState()
# ── Build ─────────────────────────────────────────────────────────────────────
output_path = '/tmp/workspace/organic-chem-pdf/Organic_Chemistry_Quick_Reference.pdf'
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2*cm, bottomMargin=2.5*cm,
title='Organic Chemistry Quick Reference',
author='Orris AI',
subject='B.Sc/B.Pharm Organic Chemistry'
)
story = []
story += build_cover()
story += build_e1_e2()
story += build_sn1_sn2()
story += build_markovnikov()
story += build_alkyl_halides()
story += build_alcohols()
story += build_condensations()
story += build_carbonyl()
story += build_carboxylic()
story += build_amines()
story += build_master_table()
doc.build(story, onFirstPage=add_footer, onLaterPages=add_footer)
print(f"PDF created: {output_path}")
PYEOF
echo "Script written successfully"
show command