#!/usr/bin/env python3 """Assemble 'The Sufi Who Fooled the Algorithm' PDF — Kiyosaki × Shah × Achebe style, Penguin 6×9in.""" import os, re, sys from reportlab.lib.pagesizes import letter from reportlab.lib.units import inch from reportlab.lib.styles import ParagraphStyle from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY from reportlab.lib.colors import HexColor from reportlab.platypus import ( Paragraph, Spacer, PageBreak, Frame, PageTemplate, BaseDocTemplate ) OUTPUT_DIR = os.path.expanduser("~/odysseus_book/shah_kiyosaki") CHAPTERS_DIR = os.path.join(OUTPUT_DIR, "chapters") PDF_PATH = os.path.join(OUTPUT_DIR, "pdf", "The_Sufi_Who_Fooled_the_Algorithm.pdf") MARGIN = 0.85 * inch FOOTER_H = 0.4 * inch PAGE_W = 6 * inch PAGE_H = 9 * inch DARK_TEAL = HexColor('#0D4F4F') GOLD = HexColor('#C8A951') DARK = HexColor('#1a1a1a') GREY = HexColor('#666666') LIGHT_GREY = HexColor('#f0f0f0') ACCENT_ORANGE = HexColor('#E67E22') style_title = ParagraphStyle('Title', fontName='Helvetica-Bold', fontSize=26, leading=32, alignment=TA_CENTER, textColor=DARK_TEAL, spaceAfter=8) style_subtitle = ParagraphStyle('Subtitle', fontName='Helvetica-Oblique', fontSize=13, leading=17, alignment=TA_CENTER, textColor=GOLD, spaceAfter=20) style_author = ParagraphStyle('Author', fontName='Helvetica', fontSize=12, leading=16, alignment=TA_CENTER, textColor=GREY, spaceAfter=28) style_copyright = ParagraphStyle('Copyright', fontName='Helvetica', fontSize=9, leading=12, alignment=TA_CENTER, textColor=GREY) style_ch_num = ParagraphStyle('ChNum', fontName='Helvetica-Bold', fontSize=10, leading=13, alignment=TA_CENTER, textColor=GOLD, spaceAfter=4, spaceBefore=18) style_ch_title = ParagraphStyle('ChTitle', fontName='Helvetica-Bold', fontSize=20, leading=26, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=8, spaceBefore=4) style_subtitle_ch = ParagraphStyle('SubtitleCh', fontName='Helvetica-Oblique', fontSize=12, leading=16, alignment=TA_LEFT, textColor=ACCENT_ORANGE, spaceAfter=14, spaceBefore=2) style_framework = ParagraphStyle('Framework', fontName='Helvetica-Bold', fontSize=11, leading=15, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=14, backColor=LIGHT_GREY, borderColor=GOLD, borderWidth=1, borderPadding=8) style_body = ParagraphStyle('Body', fontName='Times-Roman', fontSize=10, leading=14.5, alignment=TA_JUSTIFY, textColor=DARK, spaceAfter=7, firstLineIndent=0) style_dialogue = ParagraphStyle('Dialogue', fontName='Times-Roman', fontSize=10, leading=14.5, alignment=TA_LEFT, textColor=DARK, spaceAfter=5, leftIndent=24) style_shah = ParagraphStyle('Shah', fontName='Times-Bold', fontSize=10, leading=14.5, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=3, leftIndent=24) style_cfo = ParagraphStyle('CFO', fontName='Times-Bold', fontSize=10, leading=14.5, alignment=TA_LEFT, textColor=GOLD, spaceAfter=3, leftIndent=24) style_lie = ParagraphStyle('Lie', fontName='Times-Bold', fontSize=10.5, leading=15, alignment=TA_LEFT, textColor=HexColor('#C0392B'), spaceAfter=6, spaceBefore=10, backColor=HexColor('#FDF2F2'), borderColor=HexColor('#C0392B'), borderWidth=1, borderPadding=10) style_protocol = ParagraphStyle('Protocol', fontName='Times-Roman', fontSize=10, leading=14.5, alignment=TA_LEFT, textColor=DARK, spaceAfter=6, leftIndent=36) style_reflection = ParagraphStyle('Reflection', fontName='Times-Italic', fontSize=10.5, leading=15, alignment=TA_CENTER, textColor=GREY, spaceAfter=16, spaceBefore=16, backColor=HexColor('#F8F9FA'), borderColor=GREY, borderWidth=0.5, borderPadding=12) style_final = ParagraphStyle('Final', fontName='Times-Italic', fontSize=10, leading=14, alignment=TA_LEFT, textColor=GREY, spaceAfter=4, leftIndent=24) style_toc_ch = ParagraphStyle('TOCCh', fontName='Helvetica-Bold', fontSize=11, leading=17, alignment=TA_LEFT, textColor=DARK_TEAL) style_toc_sub = ParagraphStyle('TOCSub', fontName='Helvetica', fontSize=9.5, leading=13, alignment=TA_LEFT, textColor=GREY) style_hook = ParagraphStyle('Hook', fontName='Times-Italic', fontSize=10.5, leading=15, alignment=TA_LEFT, textColor=DARK, spaceAfter=10, spaceBefore=6, backColor=HexColor('#EBF5FB'), borderColor=DARK_TEAL, borderWidth=1, borderPadding=10) CHAPTERS = [ ("The Man Who Watched the Shadow", "Attention Economy, Doomscrolling, and the Shadow You Feed", "Attention Quadrant: Consumption / Curation / Creation / Contemplation"), ("Nasrudin's Lost Key", "Looking Where the Light Is — Data Bias, Streetlight Effect, and the Answers You'll Never Find", "Epistemic Quadrant: Known Knowns / Known Unknowns / Unknown Knowns / Unknown Unknowns"), ("The Man Who Ate the Menu", "Consuming Content vs. Digesting Wisdom — Information Obesity, Malnourished Minds", "Consumption Quadrant: Input / Filter / Digest / Apply"), ("The Commanding Self", "Social Media Ego, Performative Identity, and the Tyrant Inside", "Identity Quadrant: Persona / Shadow / Essence / Witness"), ("Learning How to Unlearn", "AI Era: Unlearning > Learning — Cognitive Flexibility as Survival Skill", "Unlearning Loop: Identify Assumption → Test → Discard → Replace → Test Again"), ("The Assumption of Knowledge", "Dunning-Kruger in the TikTok Era — The Illusion of Competence", "Competence Quadrant: Unconscious Incompetence → Conscious Incompetence → Conscious Competence → Unconscious Competence + Shah's Fifth"), ("The Teacher Who Refused to Teach", "Mentorship Crisis, Credentialism, and the Parrot Who Quoted Rumi", "Transmission Loop: Embody → Transmit → Verify → Release"), ("The Parrot and the Scholar", "Mimicry vs. Understanding — LLMs, Cargo Cult Science, and the Illusion of Intelligence", "Understanding Ladder: Pattern Match → Compress → Predict → Generate → Embody → Transmit"), ("The Dream That Woke the Dreamer", "Purpose, Midlife Crisis, and the Nightmare of a Life Not Lived", "Purpose Loop: Disorient → Question → Experiment → Integrate → Question Again"), ("The Story That Has No Ending", "Legacy, Algorithmic Immortality, and the Art of Leaving Incomplete", "Legacy Loop: Create → Release → Let Go → Trust → Begin Again"), ] LIGHT_GREY = HexColor('#f0f0f0') DARK_GREY = HexColor('#333333') def clean_text(text): text = text.replace('&', '&').replace('<', '<').replace('>', '>') parts = text.split('**') for i in range(1, len(parts), 2): parts[i] = f'{parts[i]}' text = ''.join(parts) parts = text.split('*') for i in range(1, len(parts), 2): parts[i] = f'{parts[i]}' text = ''.join(parts) return text def parse_chapter(md_text): lines = md_text.split('\n') sections = {'hook': [], 'dialogue': [], 'framework': [], 'lie': [], 'protocol': [], 'final': [], 'reflection': []} current = None for line in lines: stripped = line.strip() if not stripped: continue # Detect section headers low = stripped.lower() if low.startswith('# hook') or low.startswith('## hook') or low == 'hook': current = 'hook' continue elif low.startswith('# shah/cfo') or low.startswith('## shah/cfo') or low.startswith('shah/cfo') or low.startswith('dialogue'): current = 'dialogue' continue elif low.startswith('# framework') or low.startswith('## framework') or low.startswith('framework'): current = 'framework' continue elif low.startswith('# the lie') or low.startswith('## the lie') or low.startswith('the lie'): current = 'lie' continue elif low.startswith('# the protocol') or low.startswith('## the protocol') or low.startswith('protocol'): current = 'protocol' continue elif low.startswith('# shah/cfo final') or low.startswith('## shah/cfo final') or low.startswith('final word') or 'final word' in low: current = 'final' continue elif low.startswith('# reflection') or low.startswith('## reflection') or low.startswith('reflection'): current = 'reflection' continue if stripped.startswith('#'): continue if stripped == '---' or stripped == '***': continue if current: sections[current].append(stripped) for k in sections: sections[k] = '\n\n'.join(sections[k]) return sections class BookDoc(BaseDocTemplate): def __init__(self, *args, **kw): BaseDocTemplate.__init__(self, *args, **kw) self.page_count = 0 def footer_fn(canvas, doc): canvas.saveState() canvas.setFont('Helvetica', 8) canvas.setFillColor(HexColor('#999999')) canvas.drawCentredString(PAGE_W / 2, 0.35 * inch, f'— {doc.page} —') canvas.restoreState() def build_pdf(): os.makedirs(os.path.join(OUTPUT_DIR, 'pdf'), exist_ok=True) frame = Frame(MARGIN, MARGIN + FOOTER_H, PAGE_W - 2 * MARGIN, PAGE_H - 2 * MARGIN - FOOTER_H, id='normal') doc = BookDoc(PDF_PATH, pagesize=(PAGE_W, PAGE_H), leftMargin=MARGIN, rightMargin=MARGIN, topMargin=MARGIN, bottomMargin=MARGIN, title="The Sufi Who Fooled the Algorithm", author='Jauhari Che Wan') doc.addPageTemplates([PageTemplate(id='main', frames=frame, onPage=footer_fn)]) story = [] # Title page story.append(Spacer(1, PAGE_H * 0.18)) story.append(Paragraph("The Sufi Who Fooled the Algorithm", style_title)) story.append(Paragraph("Idries Shah for the Attention Age", style_subtitle)) story.append(Paragraph("10 Teaching Stories Reimagined for AI, Anxiety, and the Art of Not Being Fooled", ParagraphStyle('Tagline', fontName='Helvetica-Oblique', fontSize=12, leading=15, alignment=TA_CENTER, textColor=HexColor('#666666'), spaceAfter=24))) story.append(Spacer(1, 14)) story.append(Paragraph('Kiyosaki-Style × Shah\'s Teaching Stories × Achebe Oral Tradition', ParagraphStyle('StyleLine', fontName='Helvetica', fontSize=10, leading=13, alignment=TA_CENTER, textColor=HexColor('#888888'), spaceAfter=20))) story.append(Spacer(1, 14)) story.append(Paragraph('Jauhari Che Wan', style_author)) story.append(Spacer(1, PAGE_H * 0.1)) story.append(Paragraph('SHAH × KIYOSAKI × ACHEBE', ParagraphStyle('Tag', parent=style_copyright, fontName='Helvetica-Bold', fontSize=9, textColor=GOLD))) story.append(PageBreak()) # Copyright story.append(Spacer(1, PAGE_H * 0.28)) for line in ['SHAH × KIYOSAKI × ACHEBE', '', 'The Sufi Who Fooled the Algorithm', 'Jauhari Che Wan', '', 'Based on Idries Shah\'s Teaching Stories', 'and Robert Kiyosaki\'s Rich Dad Framework', '', 'Copyright \u00a9 2025', 'All rights reserved', '', 'Typeset in Times / Helvetica', 'Printed in Malaysia']: if line == '': story.append(Spacer(1, 4)) else: story.append(Paragraph(line, style_copyright)) story.append(PageBreak()) # Dedication story.append(Spacer(1, PAGE_H * 0.28)) story.append(Paragraph('For every mind that\'s ever been fooled by its own shadow
and had the courage to stop running.', style_dedication := ParagraphStyle('Ded', fontName='Times-Italic', fontSize=11, leading=16, alignment=TA_CENTER, textColor=DARK, spaceAfter=24))) story.append(PageBreak()) # TOC story.append(Paragraph('Contents', ParagraphStyle('TOCHeader', fontName='Helvetica-Bold', fontSize=18, leading=24, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=20))) for i, (title, subtitle, _) in enumerate(CHAPTERS, 1): story.append(Paragraph(f'Chapter {i}: {title}', style_toc_ch)) story.append(Paragraph(f' {subtitle}', style_toc_sub)) story.append(Spacer(1, 4)) story.append(PageBreak()) # Chapters for ch_num, (title, subtitle, framework) in enumerate(CHAPTERS, 1): fname = os.path.join(CHAPTERS_DIR, f"Chapter_0{ch_num}.md") if ch_num < 10 else os.path.join(CHAPTERS_DIR, f"Chapter_{ch_num}.md") if not os.path.exists(fname): print(f" WARNING: {fname} not found") continue with open(fname, 'r', encoding='utf-8') as f: md = f.read() print(f" Adding Chapter {ch_num}: {title}...") sections = parse_chapter(md) # Chapter header story.append(Paragraph(f'Chapter {ch_num}', style_ch_num)) story.append(Paragraph(title, style_ch_title)) story.append(Paragraph(subtitle, style_subtitle_ch)) story.append(Spacer(1, 10)) # HOOK if sections['hook']: hook_text = clean_text(' '.join(sections['hook'].split('\n'))) story.append(Paragraph(hook_text, style_hook)) story.append(Spacer(1, 6)) # SHAH/CFO DIALOGUE if sections['dialogue']: for para in sections['dialogue'].split('\n\n'): clean = clean_text(para.strip()) if not clean: continue low = clean.lower() if low.startswith('shah:'): story.append(Paragraph(f'SHAH: {clean[5:].strip()}', style_shah)) elif low.startswith('cfo:'): story.append(Paragraph(f'CFO: {clean[4:].strip()}', style_cfo)) else: story.append(Paragraph(clean, style_dialogue)) story.append(Spacer(1, 6)) # FRAMEWORK if sections['framework']: story.append(Paragraph(f'FRAMEWORK: {framework}', style_framework)) story.append(Spacer(1, 4)) for para in sections['framework'].split('\n\n'): clean = clean_text(para.strip()) if clean: story.append(Paragraph(clean, style_body)) story.append(Spacer(1, 6)) # THE LIE if sections['lie']: lie_lines = sections['lie'].split('\n') lie_title = lie_lines[0] if lie_lines else "THE LIE WE'RE TOLD" story.append(Paragraph(lie_title, style_lie)) lie_body = '\n'.join(lie_lines[1:]) if len(lie_lines) > 1 else '' for para in lie_body.split('\n\n'): clean = clean_text(para.strip()) if clean: story.append(Paragraph(clean, style_body)) story.append(Spacer(1, 6)) # PROTOCOL if sections['protocol']: story.append(Paragraph('THE PROTOCOL — Do This This Week', ParagraphStyle('ProtoHeader', fontName='Helvetica-Bold', fontSize=12, leading=16, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=10))) for i, para in enumerate(sections['protocol'].split('\n\n'), 1): clean = clean_text(para.strip()) if clean: if clean.lower().startswith(f'step {i}') or clean.lower().startswith(f'step {i}:'): clean = clean.split(':', 1)[-1].strip() story.append(Paragraph(f'STEP {i}: {clean}', style_protocol)) story.append(Spacer(1, 6)) # SHAH/CFO FINAL WORD if sections['final']: for para in sections['final'].split('\n\n'): clean = clean_text(para.strip()) low = clean.lower() if low.startswith('shah:'): story.append(Paragraph(f'SHAH: {clean[5:].strip()}', style_shah)) elif low.startswith('cfo:'): story.append(Paragraph(f'CFO: {clean[4:].strip()}', style_cfo)) else: story.append(Paragraph(clean, style_final)) story.append(Spacer(1, 6)) # REFLECTION if sections['reflection']: clean = clean_text(' '.join(sections['reflection'].split('\n'))) story.append(Paragraph(clean, style_reflection)) story.append(PageBreak()) print('Building PDF...') doc.build(story) print(f'Done: {PDF_PATH}') # Stats import subprocess result = subprocess.run(['wc', '-c', PDF_PATH], capture_output=True, text=True) size_kb = int(result.stdout.split()[0]) / 1024 with open(PDF_PATH, 'rb') as f: data = f.read() pages = len(re.findall(rb'/Type\s*/Page[^s]', data)) print(f'Pages: {pages}, Size: {size_kb:.0f} KB') if __name__ == '__main__': build_pdf()