Setiap awal bulan, sistem billing harus mengirim invoice ke semua klien. PDF-nya tidak boleh dibuat manual satu per satu. File kontrak yang terpecah tiga bagian juga mesti digabung jadi satu dokumen sebelum tanda tangan. Ada lagi arsip lama berupa ratusan PDF hasil scan yang teksnya perlu ditarik ke database.
Semua itu bisa diotomatisasi dengan Python. Saya pakai dua library untuk urusan begini: pypdf untuk membaca dan memanipulasi PDF yang sudah ada, dan reportlab untuk membuat PDF dari nol. Berikut contohnya, lengkap dengan kode yang bisa langsung kamu jalankan.
Install pypdf dan reportlab
Dua paket ini cukup. Install keduanya di virtualenv proyekmu. Kalau belum terbiasa memisahkan environment tiap proyek, tutorial uv menunjukkan caranya — sekalian lebih cepat daripada pip biasa.
pip install pypdf reportlab
Membaca teks dan metadata
PdfReader membuka file dan menyimpan semua halamannya. Metadata seperti judul dan author bisa dibaca lewat properti metadata, lalu teks tiap halaman ditarik lewat extract_text().
from pypdf import PdfReader
reader = PdfReader("laporan.pdf")
print(f"Jumlah halaman: {len(reader.pages)}")
meta = reader.metadata
print("Judul:", meta.title)
print("Author:", meta.author)
for i, page in enumerate(reader.pages, start=1):
text = page.extract_text()
print(f"--- Halaman {i} ---")
print(text)
PDF hasil scan tidak menyimpan teks sama sekali — isinya cuma gambar halaman. extract_text() akan mengembalikan string kosong atau karakter acak, dan tidak ada library yang bisa memulihkannya tanpa OCR. Pakai pytesseract atau ocrmypdf untuk kasus ini. Kalau tujuanmu memasukkan isi dokumen ke pipeline RAG, saya biasanya konversi dulu ke Markdown lewat anydoc.
Menggabungkan beberapa PDF jadi satu
PdfWriter menampung halaman dari banyak file lalu menulisnya sebagai satu dokumen. Urutan add_page() menentukan urutan halaman di file akhir. Kalau file-nya banyak dan tersebar di satu folder, kumpulkan dulu daftar path-nya dengan pathlib, baru iterasi seperti di bawah.
from pypdf import PdfReader, PdfWriter
writer = PdfWriter()
for path in ["cover.pdf", "bab1.pdf", "bab2.pdf", "lampiran.pdf"]:
reader = PdfReader(path)
for page in reader.pages:
writer.add_page(page)
with open("laporan_lengkap.pdf", "wb") as f:
writer.write(f)
Memisahkan PDF per halaman atau per range
Membalik prosesnya juga mudah. Loop reader.pages untuk menulis satu file per halaman, atau ambil slice reader.pages[1:5] untuk range halaman tertentu. Ingat, indeks halaman mulai dari 0.
from pypdf import PdfReader, PdfWriter
reader = PdfReader("laporan_lengkap.pdf")
# Satu file per halaman
for i, page in enumerate(reader.pages, start=1):
writer = PdfWriter()
writer.add_page(page)
with open(f"halaman_{i}.pdf", "wb") as f:
writer.write(f)
# Hanya halaman 2 sampai 5 (indeks 1..4)
writer = PdfWriter()
for page in reader.pages[1:5]:
writer.add_page(page)
with open("bab1.pdf", "wb") as f:
writer.write(f)
Merotasi halaman yang miring
Dokumen hasil scan sering miring. Properti page.rotation menyimpan sudut rotasi, dan rotate() menambah rotasi dalam derajat. Cek nilai rotation dulu supaya tidak memutar halaman yang sudah tegak.
from pypdf import PdfReader, PdfWriter
reader = PdfReader("scan.pdf")
writer = PdfWriter()
for page in reader.pages:
if page.rotation == 0:
page.rotate(90) # putar 90 derajat searah jarum jam
writer.add_page(page)
with open("scan_dirapikan.pdf", "wb") as f:
writer.write(f)
Menambah watermark
merge_page() menimpa halaman dokumen dengan konten halaman watermark. Siapkan watermark sebagai PDF satu halaman, lalu tempel ke semua halaman. Buat watermark-nya transparan supaya teks di bawahnya tetap terbaca.
from pypdf import PdfReader, PdfWriter
dokumen = PdfReader("kontrak.pdf")
watermark = PdfReader("watermark.pdf").pages[0]
writer = PdfWriter()
for page in dokumen.pages:
page.merge_page(watermark)
writer.add_page(page)
with open("kontrak_watermark.pdf", "wb") as f:
writer.write(f)
Membuat PDF dari nol dengan reportlab
reportlab menggambar PDF lewat koordinat. Canvas memberi kontrol penuh: posisi teks dihitung dari pojok kiri-bawah dalam satuan yang kamu tentukan. Berikut invoice sederhana yang bisa dipakai sebagai template.
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
c = canvas.Canvas("invoice.pdf", pagesize=A4)
w, h = A4
c.setFont("Helvetica-Bold", 16)
c.drawString(20 * mm, h - 25 * mm, "INVOICE")
c.setFont("Helvetica", 11)
c.drawString(20 * mm, h - 33 * mm, "PT Bengkel Kode")
c.drawString(20 * mm, h - 38 * mm, "No. Invoice: INV-2026-001")
c.drawString(20 * mm, h - 43 * mm, "Tanggal: 28 Oktober 2026")
c.setFont("Helvetica-Bold", 12)
c.drawString(20 * mm, h - 56 * mm, "Item")
c.drawString(120 * mm, h - 56 * mm, "Harga")
items = [
("Jasa pembuatan website", "Rp 5.000.000"),
("Maintenance 3 bulan", "Rp 1.500.000"),
]
y = h - 61 * mm
c.setFont("Helvetica", 11)
for nama, harga in items:
c.drawString(20 * mm, y, nama)
c.drawString(120 * mm, y, harga)
y -= 7 * mm
c.setFont("Helvetica-Bold", 11)
c.drawString(120 * mm, y - 3 * mm, "Total: Rp 6.500.000")
c.save()
Canvas cocok untuk dokumen sederhana seperti invoice dan kwitansi. Begitu layout mulai rumit — tabel, gambar, halaman yang mengalir — pindah ke Platypus (bagian dari reportlab) atau WeasyPrint, daripada bertarung dengan koordinat manual.
Hati-hati dengan PDF dari sumber asing. Format PDF bisa membawa JavaScript dan objek berbahaya. pypdf hanya parsing struktur file sehingga relatif aman, tapi tetap jangan proses PDF sembarangan di server tanpa validasi input terlebih dahulu.
Kenapa extract_text() mengembalikan teks kosong atau berantakan?
Hampir selalu karena PDF-nya hasil scan — yang tersimpan adalah gambar halaman, bukan lapisan teks. Tidak ada library yang bisa memulihkannya tanpa OCR. Pakai pytesseract atau ocrmypdf, atau layanan OCR yang sudah matang.
Kapan pakai pypdf dan kapan pakai reportlab?
pypdf untuk membaca, menggabungkan, memisahkan, dan memanipulasi PDF yang sudah jadi. reportlab untuk membuat PDF baru dari nol — invoice, laporan, sertifikat — saat kamu butuh kontrol penuh atas isi dokumen.
Bisakah mengisi field formulir PDF interaktif dengan Python?
Bisa. pypdf punya reader.get_fields() untuk membaca nama field dan writer.update_page_form_field_values() untuk mengisinya. Untuk form yang lebih kompleks, pdfrw juga sering dipakai.