""" مستندِ فنی را از Markdown به PDF می‌سازد: docs/technical-handbook.md → docs/technical-handbook.pdf python docs/build_handbook.py چرا (۱۰ مهر ۱۴۰۵): PDF پیش‌تر از یک HTMLِ دست‌سازِ جدا ساخته می‌شد که در مخزن نبود. هر به‌روزرسانی یعنی ویرایشِ دو نسخه، و بعد از ۹ مهر آن HTML گم شد. حالا Markdown تنها منبع است و PDF از روی آن ساخته می‌شود؛ نمودارها PNGهای `docs/images/technical-handbook/` هستند. نیاز دارد: `pandoc` (brew install pandoc) و Google Chrome. فونت (وزیرمتن، مجوزِ OFL) کنارِ همین فایل در `docs/assets/` است، پس PDF روی هر ماشینی یکسان درمی‌آید و به اینترنت نیاز ندارد. """ import base64 import shutil import subprocess import sys import tempfile from pathlib import Path DOCS = Path(__file__).resolve().parent SRC = DOCS / 'technical-handbook.md' OUT = DOCS / 'technical-handbook.pdf' CHROME_CANDIDATES = [ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', 'google-chrome', 'chromium', 'chromium-browser', ] CSS = ''' @font-face{font-family:'Vazirmatn';font-weight:100 900;src:url(data:font/woff2;base64,%(font)s) format('woff2')} @page{size:A4;margin:16mm 15mm 16mm 15mm} :root{--ink:#1d2330;--muted:#5b6472;--line:#e4e0d6;--accent:#2f55c8;--accent-soft:#eaf0ff;--code-bg:#f6f4ee} *{box-sizing:border-box} html,body{background:#fff} body{margin:0;font-family:Vazirmatn,Tahoma,sans-serif;color:var(--ink);font-size:10.6pt;line-height:1.85; -webkit-print-color-adjust:exact;print-color-adjust:exact} h1{font-size:25pt;font-weight:900;margin:0 0 6px;color:var(--ink)} h1+table{margin-top:14px} h2{font-size:17pt;font-weight:800;margin:0 0 8px;padding-top:6px;border-top:3px solid var(--accent);break-before:page;break-after:avoid} h3{font-size:12.5pt;font-weight:700;margin:20px 0 6px;color:#26305a;break-after:avoid} h4{font-size:11pt;font-weight:700;margin:12px 0 4px;break-after:avoid} p{margin:6px 0} a{color:var(--accent);text-decoration:none} code{font-family:Menlo,Consolas,monospace;font-size:.84em;direction:ltr;unicode-bidi:isolate;background:var(--code-bg); padding:1px 4px;border-radius:4px;border:1px solid #ece8dd} pre{direction:ltr;text-align:left;background:#1f2430;color:#e8e6df;border-radius:8px;padding:10px 12px; font-size:8.6pt;line-height:1.55;white-space:pre-wrap;word-break:break-word;break-inside:avoid} pre code{background:none;border:0;padding:0;color:inherit;font-size:1em} table{width:100%%;border-collapse:collapse;margin:8px 0 12px;font-size:9.4pt;break-inside:auto} th,td{border:1px solid var(--line);padding:5px 8px;text-align:right;vertical-align:top} th{background:#f4f1e8;font-weight:700} tr{break-inside:avoid} blockquote{margin:10px 0;padding:8px 14px;border:1px solid #d9e2fb;border-right:4px solid var(--accent); background:var(--accent-soft);border-radius:8px;break-inside:avoid} blockquote h4{margin-top:2px;color:var(--accent)} img{max-width:100%%;display:block;margin:10px auto 4px;border-radius:6px} img[src*="screenshot-"]{max-width:62mm;border:1px solid var(--line);box-shadow:0 2px 8px rgba(30,40,80,.08)} p:has(> img){break-inside:avoid;margin:0} p > em:only-child{display:block;font-size:9pt;color:var(--muted);text-align:center;margin:2px 0 12px} ul,ol{padding-right:20px;margin:6px 0} li{margin:2px 0} hr{border:0;border-top:1px solid var(--line);margin:14px 0} ''' def chrome() -> str: for c in CHROME_CANDIDATES: if Path(c).exists() or shutil.which(c): return c sys.exit('Chrome پیدا نشد؛ مسیرش را در CHROME_CANDIDATES بگذارید.') def main(): if not shutil.which('pandoc'): sys.exit('pandoc نصب نیست: brew install pandoc') font = base64.b64encode((DOCS / 'assets' / 'Vazirmatn.woff2').read_bytes()).decode() body = subprocess.run(['pandoc', str(SRC), '-f', 'gfm', '-t', 'html5'], capture_output=True, text=True, check=True).stdout html = (f'' f'مستند فنی تدریسینو' f'{body}') # فایلِ موقت کنارِ docs تا مسیرِ نسبیِ عکس‌ها (images/…) درست باز شود. with tempfile.NamedTemporaryFile('w', suffix='.html', dir=DOCS, delete=False, encoding='utf-8') as f: f.write(html) tmp = Path(f.name) try: subprocess.run([chrome(), '--headless=new', '--disable-gpu', '--no-pdf-header-footer', '--virtual-time-budget=10000', f'--print-to-pdf={OUT}', tmp.as_uri()], check=True, capture_output=True) finally: tmp.unlink(missing_ok=True) print(f'{OUT} ({OUT.stat().st_size // 1024} KB)') if __name__ == '__main__': main()