<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://onnocenter.or.id/wiki/index.php?action=history&amp;feed=atom&amp;title=Python%3A_Nulis_nama_di_PDF_Sertifikat</id>
	<title>Python: Nulis nama di PDF Sertifikat - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://onnocenter.or.id/wiki/index.php?action=history&amp;feed=atom&amp;title=Python%3A_Nulis_nama_di_PDF_Sertifikat"/>
	<link rel="alternate" type="text/html" href="https://onnocenter.or.id/wiki/index.php?title=Python:_Nulis_nama_di_PDF_Sertifikat&amp;action=history"/>
	<updated>2026-04-21T02:52:06Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.4</generator>
	<entry>
		<id>https://onnocenter.or.id/wiki/index.php?title=Python:_Nulis_nama_di_PDF_Sertifikat&amp;diff=73527&amp;oldid=prev</id>
		<title>Onnowpurbo: Created page with &quot;Bisa. Cara paling aman biasanya begini:  * siapkan '''1 file template sertifikat PDF''' * siapkan '''1 file daftar nama''' * jalankan script untuk membuat '''1 PDF per nama'''...&quot;</title>
		<link rel="alternate" type="text/html" href="https://onnocenter.or.id/wiki/index.php?title=Python:_Nulis_nama_di_PDF_Sertifikat&amp;diff=73527&amp;oldid=prev"/>
		<updated>2026-04-20T06:16:16Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;Bisa. Cara paling aman biasanya begini:  * siapkan &amp;#039;&amp;#039;&amp;#039;1 file template sertifikat PDF&amp;#039;&amp;#039;&amp;#039; * siapkan &amp;#039;&amp;#039;&amp;#039;1 file daftar nama&amp;#039;&amp;#039;&amp;#039; * jalankan script untuk membuat &amp;#039;&amp;#039;&amp;#039;1 PDF per nama&amp;#039;&amp;#039;&amp;#039;...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Bisa. Cara paling aman biasanya begini:&lt;br /&gt;
&lt;br /&gt;
* siapkan '''1 file template sertifikat PDF'''&lt;br /&gt;
* siapkan '''1 file daftar nama'''&lt;br /&gt;
* jalankan script untuk membuat '''1 PDF per nama'''&lt;br /&gt;
&lt;br /&gt;
Di bawah ini script Python yang umum dipakai. Script ini akan:&lt;br /&gt;
&lt;br /&gt;
* membaca nama dari file `nama.txt`&lt;br /&gt;
* menaruh nama di posisi tertentu pada template PDF&lt;br /&gt;
* menghasilkan file baru seperti `sertifikat_Andi.pdf`, `sertifikat_Budi.pdf`, dst.&lt;br /&gt;
&lt;br /&gt;
 import os&lt;br /&gt;
 import re&lt;br /&gt;
 import io&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 from pypdf import PdfReader, PdfWriter&lt;br /&gt;
 from reportlab.pdfgen import canvas&lt;br /&gt;
 from reportlab.pdfbase.ttfonts import TTFont&lt;br /&gt;
 from reportlab.pdfbase import pdfmetrics&lt;br /&gt;
 &lt;br /&gt;
 # =========================&lt;br /&gt;
 # KONFIGURASI&lt;br /&gt;
 # =========================&lt;br /&gt;
 TEMPLATE_PDF = &amp;quot;template_sertifikat.pdf&amp;quot;&lt;br /&gt;
 DAFTAR_NAMA = &amp;quot;nama.txt&amp;quot;&lt;br /&gt;
 OUTPUT_DIR = &amp;quot;output_sertifikat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # Posisi nama pada halaman PDF&lt;br /&gt;
 # Satuan: points&lt;br /&gt;
 # Titik (0,0) ada di kiri bawah halaman&lt;br /&gt;
 NAMA_X = 297   # contoh tengah halaman A4 landscape&lt;br /&gt;
 NAMA_Y = 250&lt;br /&gt;
 &lt;br /&gt;
 FONT_NAME = &amp;quot;Helvetica-Bold&amp;quot;&lt;br /&gt;
 FONT_SIZE = 28&lt;br /&gt;
 TEXT_COLOR = (0, 0, 0)  # RGB 0-1 akan di-set di bawah&lt;br /&gt;
 &lt;br /&gt;
 # Jika ingin pakai font TTF sendiri, uncomment ini:&lt;br /&gt;
 # FONT_TTF_PATH = &amp;quot;Montserrat-Bold.ttf&amp;quot;&lt;br /&gt;
 # pdfmetrics.registerFont(TTFont(&amp;quot;CustomFont&amp;quot;, FONT_TTF_PATH))&lt;br /&gt;
 # FONT_NAME = &amp;quot;CustomFont&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # =========================&lt;br /&gt;
 # FUNGSI BANTUAN&lt;br /&gt;
 # =========================&lt;br /&gt;
 def safe_filename(text: str) -&amp;gt; str:&lt;br /&gt;
     text = text.strip()&lt;br /&gt;
     text = re.sub(r&amp;quot;[^\w\s-]&amp;quot;, &amp;quot;&amp;quot;, text, flags=re.UNICODE)&lt;br /&gt;
     text = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot;_&amp;quot;, text)&lt;br /&gt;
     return text&lt;br /&gt;
 &lt;br /&gt;
 def buat_overlay(page_width, page_height, nama):&lt;br /&gt;
     packet = io.BytesIO()&lt;br /&gt;
     c = canvas.Canvas(packet, pagesize=(page_width, page_height)) &lt;br /&gt;
 &lt;br /&gt;
     c.setFont(FONT_NAME, FONT_SIZE)&lt;br /&gt;
     c.setFillColorRGB(*TEXT_COLOR) &lt;br /&gt;
 &lt;br /&gt;
     # Tulis nama dengan anchor tengah&lt;br /&gt;
     text_width = pdfmetrics.stringWidth(nama, FONT_NAME, FONT_SIZE)&lt;br /&gt;
     c.drawString(NAMA_X - (text_width / 2), NAMA_Y, nama) &lt;br /&gt;
 &lt;br /&gt;
     c.save()&lt;br /&gt;
     packet.seek(0)&lt;br /&gt;
     return PdfReader(packet)&lt;br /&gt;
 &lt;br /&gt;
 def baca_daftar_nama(path_file):&lt;br /&gt;
     with open(path_file, &amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:&lt;br /&gt;
         return [line.strip() for line in f if line.strip()] &lt;br /&gt;
 &lt;br /&gt;
 # =========================&lt;br /&gt;
 # PROSES UTAMA&lt;br /&gt;
 # =========================&lt;br /&gt;
 def main():&lt;br /&gt;
     os.makedirs(OUTPUT_DIR, exist_ok=True) &lt;br /&gt;
 &lt;br /&gt;
     names = baca_daftar_nama(DAFTAR_NAMA)&lt;br /&gt;
     if not names:&lt;br /&gt;
         raise ValueError(&amp;quot;File nama.txt kosong atau tidak ada nama valid.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     template_reader = PdfReader(TEMPLATE_PDF)&lt;br /&gt;
     if len(template_reader.pages) == 0:&lt;br /&gt;
         raise ValueError(&amp;quot;Template PDF tidak memiliki halaman.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     template_page = template_reader.pages[0]&lt;br /&gt;
     page_width = float(template_page.mediabox.width)&lt;br /&gt;
     page_height = float(template_page.mediabox.height) &lt;br /&gt;
 &lt;br /&gt;
     for nama in names:&lt;br /&gt;
         reader = PdfReader(TEMPLATE_PDF)&lt;br /&gt;
         writer = PdfWriter() &lt;br /&gt;
 &lt;br /&gt;
         page = reader.pages[0]&lt;br /&gt;
         overlay_pdf = buat_overlay(page_width, page_height, nama)&lt;br /&gt;
         overlay_page = overlay_pdf.pages[0]&lt;br /&gt;
 &lt;br /&gt;
         page.merge_page(overlay_page)&lt;br /&gt;
         writer.add_page(page)&lt;br /&gt;
 &lt;br /&gt;
         # Jika template punya lebih dari 1 halaman, tambahkan sisanya&lt;br /&gt;
         for i in range(1, len(reader.pages)):&lt;br /&gt;
             writer.add_page(reader.pages[i])&lt;br /&gt;
 &lt;br /&gt;
         nama_file = safe_filename(nama)&lt;br /&gt;
         output_path = Path(OUTPUT_DIR) / f&amp;quot;sertifikat_{nama_file}.pdf&amp;quot; &lt;br /&gt;
 &lt;br /&gt;
         with open(output_path, &amp;quot;wb&amp;quot;) as out_file:&lt;br /&gt;
             writer.write(out_file)&lt;br /&gt;
 &lt;br /&gt;
         print(f&amp;quot;Berhasil membuat: {output_path}&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
Siapkan file `nama.txt` seperti ini:&lt;br /&gt;
&lt;br /&gt;
 Andi Saputra&lt;br /&gt;
 Budi Santoso&lt;br /&gt;
 Citra Lestari&lt;br /&gt;
 Dewi Anggraini&lt;br /&gt;
&lt;br /&gt;
Install library yang dibutuhkan:&lt;br /&gt;
&lt;br /&gt;
 pip install pypdf reportlab&lt;br /&gt;
&lt;br /&gt;
Lalu jalankan:&lt;br /&gt;
&lt;br /&gt;
 python buat_sertifikat.py&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hal penting:&lt;br /&gt;
&lt;br /&gt;
* `NAMA_X` dan `NAMA_Y` harus disesuaikan dengan posisi area nama di sertifikat.&lt;br /&gt;
* Kalau teks nama terlalu panjang, bisa:&lt;br /&gt;
&lt;br /&gt;
  * kecilkan `FONT_SIZE`&lt;br /&gt;
  * ganti font&lt;br /&gt;
  * atau pakai logika auto-fit.&lt;br /&gt;
&lt;br /&gt;
Kalau Anda mau, saya bisa lanjut buatkan versi yang lebih lengkap, misalnya:&lt;br /&gt;
&lt;br /&gt;
* baca dari '''Excel/CSV'''&lt;br /&gt;
* otomatis '''mengecilkan font''' jika nama kepanjangan&lt;br /&gt;
* tambah '''nomor sertifikat''', '''tanggal''', dan '''judul kegiatan'''&lt;br /&gt;
* atau saya sesuaikan langsung berdasarkan '''template PDF''' yang Anda upload.&lt;/div&gt;</summary>
		<author><name>Onnowpurbo</name></author>
	</entry>
</feed>