Skip to content

Commit e9035aa

Browse files
committed
add convert to pdf tool
1 parent 10b7602 commit e9035aa

2 files changed

Lines changed: 117 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ tools/ai_eval/output
174174
tools/ai_eval/responses.json
175175
tools/course_stats/output
176176
tools/course_stats/data
177-
177+
tools/to_pdf/**/*.pdf
178178

179179
# vscode
180180
.vscode/

tools/to_pdf/main.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import os
2+
import requests
3+
from bs4 import BeautifulSoup
4+
from urllib.parse import urljoin, urlparse
5+
import pdfkit
6+
from PyPDF2 import PdfMerger, PdfReader, PdfWriter
7+
import re
8+
9+
def natural_key(s):
10+
"""Turn a string into a list of ints and text, so 's10' > 's9'."""
11+
return [int(text) if text.isdigit() else text.lower()
12+
for text in re.split(r'(\d+)', s)]
13+
14+
15+
def get_all_links(base_url):
16+
"""Crawl all internal links from base_url."""
17+
visited = set()
18+
to_visit = [base_url]
19+
urls = []
20+
21+
while to_visit:
22+
url = to_visit.pop()
23+
if url in visited:
24+
continue
25+
visited.add(url)
26+
27+
try:
28+
r = requests.get(url)
29+
r.raise_for_status()
30+
except Exception as e:
31+
print(f"❌ Failed to fetch {url}: {e}")
32+
continue
33+
34+
soup = BeautifulSoup(r.text, "html.parser")
35+
urls.append(url)
36+
37+
for a in soup.find_all("a", href=True):
38+
href = urljoin(base_url, a["href"])
39+
# Only keep pages within same domain
40+
if urlparse(href).netloc == urlparse(base_url).netloc:
41+
if href not in visited and href not in to_visit and "#" not in href:
42+
to_visit.append(href)
43+
return urls
44+
45+
46+
def save_pages_as_pdfs(urls, output_dir="pages_pdfs"):
47+
os.makedirs(output_dir, exist_ok=True)
48+
pdf_files = []
49+
for i, url in enumerate(urls):
50+
out_file = os.path.join(output_dir, f"page_{i+1}.pdf")
51+
try:
52+
pdfkit.from_url(url, out_file)
53+
pdf_files.append(out_file)
54+
print(f"✅ Saved {url}{out_file}")
55+
except Exception as e:
56+
print(f"❌ Failed to render {url}: {e}")
57+
return pdf_files
58+
59+
60+
def merge_pdfs(pdf_files, output_file="combined.pdf"):
61+
merger = PdfMerger()
62+
for pdf in pdf_files:
63+
merger.append(pdf)
64+
merger.write(output_file)
65+
merger.close()
66+
print(f"📚 Combined PDF saved as {output_file}")
67+
68+
69+
import subprocess
70+
71+
def compress_pdf(input_file, output_file, quality="/ebook"):
72+
subprocess.run([
73+
"gs", "-sDEVICE=pdfwrite", "-dCompatibilityLevel=1.4",
74+
f"-dPDFSETTINGS={quality}",
75+
"-dNOPAUSE", "-dQUIET", "-dBATCH",
76+
f"-sOutputFile={output_file}", input_file
77+
])
78+
print(f"📉 Compressed {input_file}{output_file}")
79+
80+
81+
82+
def split_pdf(input_file, output_files):
83+
reader = PdfReader(input_file)
84+
total_pages = len(reader.pages)
85+
num_splits = len(output_files)
86+
pages_per_split = total_pages // num_splits
87+
remainder = total_pages % num_splits
88+
89+
start = 0
90+
for i, out_file in enumerate(output_files):
91+
# Distribute the remainder pages one by one to the first splits
92+
end = start + pages_per_split + (1 if i < remainder else 0)
93+
writer = PdfWriter()
94+
for page in reader.pages[start:end]:
95+
writer.add_page(page)
96+
with open(out_file, "wb") as f:
97+
writer.write(f)
98+
print(f"✂️ Split pages {start+1}-{end}{out_file}")
99+
start = end
100+
101+
102+
if __name__ == "__main__":
103+
base_url = "https://skaftenicki.github.io/dtu_mlops/"
104+
urls = get_all_links(base_url)
105+
urls = sorted(urls, key=natural_key) # 👈 use natural sort
106+
print(f"Found {len(urls)} pages.")
107+
108+
pdf_files = save_pages_as_pdfs(urls)
109+
merge_pdfs(pdf_files, "dtu_mlops_all.pdf")
110+
split_pdf("dtu_mlops_all.pdf", [
111+
"dtu_mlops_part1.pdf", "dtu_mlops_part2.pdf", "dtu_mlops_part3.pdf", "dtu_mlops_part4.pdf"
112+
])
113+
compress_pdf("dtu_mlops_part1.pdf", "dtu_mlops_part1_small.pdf")
114+
compress_pdf("dtu_mlops_part2.pdf", "dtu_mlops_part2_small.pdf")
115+
compress_pdf("dtu_mlops_part3.pdf", "dtu_mlops_part3_small.pdf")
116+
compress_pdf("dtu_mlops_part4.pdf", "dtu_mlops_part4_small.pdf")

0 commit comments

Comments
 (0)