Spaces:
Sleeping
Sleeping
Commit ·
a2a4ece
1
Parent(s): d31e132
docs: add tools/build_readme_pdf.py (PDF gitignored — HF rejects binaries)
Browse filesThe HF Space pre-receive hook rejects binary files in the Space repo
(use Xet/LFS or releases instead). Putting README.pdf in the gitignore
keeps both remotes in sync at the same tree.
To regenerate the PDF locally for sharing:
uv pip install --python ~/.cache/uv-venvs/insurance-sales-bot/bin/python markdown-pdf
python tools/build_readme_pdf.py
# → README.pdf (~730 KB, ready to email / Slack)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- .gitignore +5 -0
- tools/build_readme_pdf.py +80 -0
.gitignore
CHANGED
|
@@ -77,3 +77,8 @@ Thumbs.db
|
|
| 77 |
|
| 78 |
# Local backup of HF dataset (not for git — too large)
|
| 79 |
rag/_hf_dataset_backup/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
# Local backup of HF dataset (not for git — too large)
|
| 79 |
rag/_hf_dataset_backup/
|
| 80 |
+
|
| 81 |
+
# README.pdf is the build artifact from tools/build_readme_pdf.py;
|
| 82 |
+
# HF Space rejects binaries in the repo (use Xet/Releases instead).
|
| 83 |
+
# Regenerate locally any time via 'python tools/build_readme_pdf.py'.
|
| 84 |
+
README.pdf
|
tools/build_readme_pdf.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Render README.md → README.pdf.
|
| 2 |
+
|
| 3 |
+
Pure-Python: `markdown-pdf` package (pymupdf-backed) — no system deps like
|
| 4 |
+
cairo/pango. Trade-off vs WeasyPrint: slightly less CSS-perfect output but
|
| 5 |
+
renders all the README's tables, code fences, headings, and links cleanly
|
| 6 |
+
without needing Homebrew or apt to install glib/pango libraries.
|
| 7 |
+
|
| 8 |
+
Run:
|
| 9 |
+
uv pip install --python ~/.cache/uv-venvs/insurance-sales-bot/bin/python markdown-pdf
|
| 10 |
+
python tools/build_readme_pdf.py
|
| 11 |
+
|
| 12 |
+
Output: README.pdf in the repo root, ready to share / download from GitHub.
|
| 13 |
+
"""
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import re
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
|
| 19 |
+
from markdown_pdf import MarkdownPdf, Section
|
| 20 |
+
|
| 21 |
+
ROOT = Path(__file__).resolve().parent.parent
|
| 22 |
+
README_PATH = ROOT / "README.md"
|
| 23 |
+
OUT_PATH = ROOT / "README.pdf"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def strip_yaml_frontmatter(text: str) -> str:
|
| 27 |
+
"""HF Spaces' YAML metadata at the top of README is meaningless in the PDF."""
|
| 28 |
+
if not text.startswith("---"):
|
| 29 |
+
return text
|
| 30 |
+
end = text.find("\n---", 3)
|
| 31 |
+
if end == -1:
|
| 32 |
+
return text
|
| 33 |
+
return text[end + 4:].lstrip()
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def strip_anchor_links(text: str) -> str:
|
| 37 |
+
"""Convert `[Text](#anchor)` → `Text` so pymupdf doesn't blow up looking
|
| 38 |
+
for the link target. External `[Text](https://…)` links stay intact."""
|
| 39 |
+
return re.sub(r"\[([^\]]+)\]\(#[^)]+\)", r"\1", text)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
CSS = """
|
| 43 |
+
body { font-family: -apple-system, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 11pt; color: #1f2937; line-height: 1.55; }
|
| 44 |
+
h1 { font-size: 22pt; color: #0f172a; margin-top: 0.5em; margin-bottom: 0.3em; border-bottom: 2px solid #0f766e; padding-bottom: 4px; }
|
| 45 |
+
h2 { font-size: 16pt; color: #134e4a; margin-top: 1.4em; margin-bottom: 0.4em; border-bottom: 1px solid #94a3b8; padding-bottom: 3px; }
|
| 46 |
+
h3 { font-size: 13pt; color: #0f766e; margin-top: 1.2em; margin-bottom: 0.3em; }
|
| 47 |
+
h4 { font-size: 11.5pt; color: #1e3a8a; margin-top: 1em; margin-bottom: 0.3em; }
|
| 48 |
+
p { margin: 0.4em 0; }
|
| 49 |
+
a { color: #0d6efd; text-decoration: none; }
|
| 50 |
+
code { font-family: 'SF Mono', Menlo, Monaco, Consolas, monospace; font-size: 9.5pt; background-color: #f1f5f9; padding: 1px 4px; border-radius: 3px; color: #b91c1c; }
|
| 51 |
+
pre { font-family: 'SF Mono', Menlo, Monaco, Consolas, monospace; font-size: 9.5pt; background-color: #f8fafc; border-left: 3px solid #0f766e; padding: 8px 10px; border-radius: 4px; line-height: 1.4; }
|
| 52 |
+
pre code { background: transparent; padding: 0; color: #0f172a; }
|
| 53 |
+
blockquote { border-left: 3px solid #94a3b8; margin: 0.8em 0; padding: 4px 12px; color: #475569; background: #f8fafc; }
|
| 54 |
+
table { border-collapse: collapse; width: 100%; margin: 0.6em 0; font-size: 10pt; }
|
| 55 |
+
th, td { border: 1px solid #cbd5e1; padding: 5px 8px; text-align: left; vertical-align: top; }
|
| 56 |
+
th { background-color: #f1f5f9; font-weight: 600; color: #0f172a; }
|
| 57 |
+
ul, ol { padding-left: 20px; margin: 0.4em 0; }
|
| 58 |
+
li { margin: 0.2em 0; }
|
| 59 |
+
hr { border: none; border-top: 1px solid #cbd5e1; margin: 1.5em 0; }
|
| 60 |
+
strong { color: #0f172a; }
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def main() -> None:
|
| 65 |
+
raw = README_PATH.read_text(encoding="utf-8")
|
| 66 |
+
cleaned = strip_anchor_links(strip_yaml_frontmatter(raw))
|
| 67 |
+
|
| 68 |
+
pdf = MarkdownPdf(toc_level=0, optimize=True)
|
| 69 |
+
pdf.meta["title"] = "Insurance Sales Portfolio Expert — README"
|
| 70 |
+
pdf.meta["author"] = "Rohit Saraf"
|
| 71 |
+
pdf.meta["subject"] = "Voice-first AI advisor for Indian health insurance — Sarvam AI takehome"
|
| 72 |
+
pdf.add_section(Section(cleaned, toc=False), user_css=CSS)
|
| 73 |
+
pdf.save(str(OUT_PATH))
|
| 74 |
+
|
| 75 |
+
size_kb = OUT_PATH.stat().st_size / 1024
|
| 76 |
+
print(f"wrote {OUT_PATH.relative_to(ROOT)} ({size_kb:.1f} KB)")
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
if __name__ == "__main__":
|
| 80 |
+
main()
|