Node.js ile HTML’den PDF Oluşturma (Puppeteer Olmadan)
Node.js uygulamalarında HTML içeriğini PDF formatına dönüştürme ihtiyacı, fatura oluşturmadan raporlamaya, bilet veya sertifika basımına kadar pek çok senaryoda karşımıza çıkar. Bu süreç, kullanıcıya sunulan dinamik verileri daha taşınabilir ve yazdırılabilir bir formata dönüştürmenin etkili bir yoludur. Genellikle bu tür görevler için akla ilk gelen çözümlerden biri Puppeteer olsa da, bu güçlü aracın beraberinde getirdiği bazı dezavantajlar bulunmaktadır. Özellikle sunucu ortamlarında kaynak tüketimi ve kurulum karmaşıklığı, geliştiricileri Puppeteer dışındaki daha hafif ve bağımsız alternatiflere yöneltmektedir. Bu makalede, Puppeteer’ın getirdiği yükten kaçınarak Node.js ile HTML’den nasıl PDF oluşturabileceğimizi, farklı yaklaşımları ve popüler kütüphaneleri detaylıca inceleyeceğiz.
Neden Puppeteer Olmadan PDF Oluşturmalıyız?
Puppeteer, Google Chrome’un headless (başsız) modunu kullanarak web sayfalarını otomatikleştiren güçlü bir kütüphanedir. PDF oluşturma konusunda da mükemmel sonuçlar verse de, bazı senaryolarda Puppeteer’sız bir yaklaşım tercih etmek daha akıllıca olabilir.
Performans ve Kaynak Tüketimi
Puppeteer, altında tam bir Chromium tarayıcı örneğini çalıştırır. Bu durum, PDF oluşturma işlemi sırasında önemli miktarda CPU ve RAM kaynağı tüketimine yol açabilir. Özellikle yoğun PDF oluşturma talepleri olan sunucu ortamlarında, her bir PDF isteği için yeni bir tarayıcı örneği başlatmak veya mevcut örneği yönetmek, sunucunun performansını ciddi şekilde etkileyebilir ve maliyetleri artırabilir. Daha az kaynak tüketen alternatifler, ölçeklenebilirlik açısından büyük avantaj sağlar.
Bağımlılıklar ve Kurulum Karmaşıklığı
Puppeteer’ın çalışabilmesi için sistemde uyumlu bir Chromium sürümünün bulunması veya indirilmesi gerekir. Bu, geliştirme ve üretim ortamları arasında sürüm uyumsuzluklarına, ek indirme sürelerine ve farklı işletim sistemlerinde (özellikle Linux sunucularda gerekli olan ek kütüphaneler) kurulum sorunlarına yol açabilir. Docker konteynerleri gibi izole ortamlarda bile, Chromium’un büyük boyutu ve bağımlılıkları, Docker imaj boyutunu artırarak dağıtım süreçlerini yavaşlatabilir.
Hafif ve Bağımsız Çözüm Arayışı
Bazı projelerde, sadece HTML’i PDF’e dönüştürmek gibi spesifik bir görev için tam bir tarayıcı motoruna ihtiyaç duyulmaz. Bu gibi durumlarda, daha az bağımlılığa sahip, daha hafif ve kurulumu daha kolay çözümler aramak mantıklıdır. Bu tür çözümler, genellikle daha hızlı başlangıç süreleri sunar ve genel sistem karmaşıklığını azaltır, bu da bakım ve hata ayıklama süreçlerini basitleştirir.
Temel Yaklaşımlar ve Kütüphaneler
Puppeteer gibi bir headless tarayıcı kullanmadan HTML’den PDF oluşturmak için farklı stratejiler ve kütüphaneler mevcuttur. Bu yaklaşımlar genellikle altta yatan farklı teknolojilere dayanır.
wkhtmltopdf Tabanlı Çözümler
wkhtmltopdf, WebKit rendering engine’ini (Chrome’un eski versiyonlarının kullandığı motor) kullanarak HTML’i PDF’e veya görüntüye dönüştüren açık kaynaklı bir komut satırı aracıdır. Puppeteer’dan farklı olarak, wkhtmltopdf tam bir tarayıcı değildir; daha çok bir dönüştürme motorudur. Node.js ekosisteminde, bu aracı sarmalayan (wrapper) kütüphaneler oldukça popülerdir.
* Artıları: Genellikle iyi bir HTML ve CSS desteği sunar, Puppeteer’a göre daha az kaynak tüketir ve kurulumu daha basittir (sadece wkhtmltopdf ikili dosyasının sistemde bulunması yeterlidir).
* Eksileri: WebKit motoru modern tarayıcılardan biraz daha eski olduğu için, en yeni CSS özellikleri veya JavaScript API’leri için tam destek sunmayabilir. Ayrıca, wkhtmltopdf‘in kendisinin sistemde yüklü olması bir dış bağımlılıktır.
HTML Ayrıştırma ve PDF Kütüphaneleri
Bu yaklaşım, HTML içeriğini ayrıştırıp (parse edip) doğrudan PDF formatına yazan kütüphaneleri içerir. pdfkit gibi kütüphaneler, Node.js ortamında programatik olarak PDF belgeleri oluşturmak için tasarlanmıştır. Ancak, pdfkit doğrudan HTML’i PDF’e dönüştürme yeteneğine sahip değildir. Bunun yerine, HTML’i ayrıştırıp (örneğin, bir DOM parser ile) içeriği (metin, resimler, tablolar) çıkarmanız ve ardından bu içeriği pdfkit API’lerini kullanarak PDF belgesine manuel olarak yerleştirmeniz gerekir.
* Artıları: Tam kontrol sağlar, dış bağımlılıkları minimuma indirir (sadece Node.js kütüphaneleri).
* Eksileri: HTML’den PDF’e dönüştürme mantığını sıfırdan yazmak, özellikle karmaşık HTML ve CSS yapılarında oldukça zaman alıcı ve zahmetli olabilir. Stil ve düzeni korumak çok zordur.
Diğer Alternatifler (Kısa Bahis)
* phantomjs Tabanlı Çözümler: phantomjs de headless WebKit tabanlı bir tarayıcıydı. Ancak projesi 2018’den beri aktif olarak geliştirilmemektedir ve günümüz modern web standartları için yetersiz kalmaktadır. Bu nedenle yeni projelerde tercih edilmemelidir.
* Ticari API’ler ve Mikroservisler: Bazı durumlarda, PDF oluşturma işlemini bir üçüncü taraf API’ye veya ayrı bir mikroservise devretmek mantıklı olabilir. Örneğin, Gotenberg (Docker tabanlı bir PDF dönüştürme mikroservisi) veya DocRaptor gibi ticari hizmetler, yüksek ölçeklenebilirlik ve bakım kolaylığı sunabilir. Bu çözümler genellikle kendi tarayıcı motorlarını veya wkhtmltopdf gibi araçları arka planda kullanır.
html-pdf Kütüphanesi ile Adım Adım PDF Oluşturma
Node.js ekosisteminde wkhtmltopdf tabanlı çözümler arasında en popüler ve kullanımı kolay olanlardan biri html-pdf kütüphanesidir. Bu kütüphane, HTML stringlerini veya dosyalarını kolayca PDF’e dönüştürmenizi sağlar.
html-pdf Nedir ve Nasıl Çalışır?
html-pdf kütüphanesi, adından da anlaşılacağı gibi HTML içeriğini PDF’e dönüştürmek için tasarlanmıştır. Temel olarak, sisteminizde yüklü olan wkhtmltopdf ikili dosyasını Node.js üzerinden çağırarak bu dönüştürme işlemini gerçekleştirir. Eğer wkhtmltopdf yüklü değilse veya kütüphane onu bulamazsa, varsayılan olarak phantomjs‘i kullanmaya çalışır (ancak bu, yukarıda belirtildiği gibi artık önerilmez). Bu nedenle, html-pdf kullanırken wkhtmltopdf‘in doğru şekilde kurulu ve erişilebilir olduğundan emin olmak kritik öneme sahiptir.
Kurulum ve Temel Kullanım
İlk adım olarak, html-pdf kütüphanesini projenize eklemeniz gerekir:
npm install html-pdf
Ardından, wkhtmltopdf'in sisteminizde kurulu olduğundan emin olun. Kurulum talimatları işletim sisteminize göre değişir:
* Linux (Debian/Ubuntu):
sudo apt-get update
sudo apt-get install wkhtmltopdf
*Not: Bazı dağıtımlarda wkhtmltopdf'in varsayılan sürümü eski olabilir veya başlık/altbilgi gibi özellikler için tam destek sunmayabilir. Bu durumda, resmi web sitesinden statik olarak derlenmiş sürümü indirip /usr/local/bin gibi bir yere kopyalamanız gerekebilir.*
* macOS:
brew install wkhtmltopdf
* Windows:
wkhtmltopdf'in resmi web sitesinden (wkhtmltopdf.org) kurulum dosyasını indirip kurmanız yeterlidir. Kurulum sırasında PATH'e eklediğinizden emin olun.
Şimdi basit bir HTML string'inden PDF oluşturma örneğine bakalım:
const pdf = require('html-pdf');
const fs = require('fs');
const htmlContent = Merhaba Dünya!
Bu, Node.js ile HTML'den oluşturulmuş basit bir PDF belgesidir.
- Öğe 1
- Öğe 2
- Öğe 3
;
const options = {
format: 'A4',
orientation: 'portrait',
border: '10mm'
};
pdf.create(htmlContent, options).toFile('./output.pdf', function(err, res) {
if (err) return console.log(err);
console.log(res); // { filename: '/path/to/output.pdf' }
console.log('PDF başarıyla oluşturuldu!');
});
Bu kod parçası, htmlContent değişkenindeki HTML string'ini alıp, belirtilen seçeneklerle output.pdf adında bir dosya oluşturur.
Seçenekler ve Özelleştirme
html-pdf kütüphanesi, PDF çıktısını özelleştirmek için zengin bir options nesnesi sunar. İşte sıkça kullanılan bazı seçenekler:
* format: Sayfa boyutu (örneğin, 'A4', 'Letter', 'Tabloid').
* orientation: Sayfa yönü ('portrait' veya 'landscape').
* border: Sayfa kenar boşlukları (örneğin, '10mm', '0.5in', { top: '1in', right: '1in', bottom: '1in', left: '1in' }).
* header: Başlık içeriği ve yüksekliği.
* footer: Altbilgi içeriği ve yüksekliği.
* base: HTML içinde göreceli yollarla belirtilen resimler veya CSS dosyaları için temel URL.
* zoom: PDF içeriğinin yakınlaştırma seviyesi.
* timeout: PDF oluşturma işlemi için maksimum süre.
Örnek bir options nesnesi:
const options = {
format: 'A4',
orientation: 'portrait',
border: {
top: '1in',
right: '1in',
bottom: '1in',
left: '1in'
},
header: {
height: '20mm',
contents: 'Üst Bilgi'
},
footer: {
height: '20mm',
contents: {
first: 'Sayfa 1',
2: 'İkinci Sayfa', // sayfa 2
default: '{{page}}/{{pages}}', // varsayılan
last: 'Son Sayfa'
}
},
// wkhtmltopdf'in yolunu manuel olarak belirtmek isterseniz:
// phantomPath: '/usr/local/bin/phantomjs', // Eğer wkhtmltopdf kullanmıyorsanız
// wkhtmltopdf: '/usr/local/bin/wkhtmltopdf' // wkhtmltopdf yolunu belirtmek için
};
Dinamik İçerik ve Şablonlar
Gerçek dünya uygulamalarında, PDF'e dönüştüreceğiniz HTML içeriği genellikle dinamik verilerle oluşturulur. Bu senaryoda, Handlebars, EJS veya Pug gibi bir şablon motoru kullanmak oldukça yaygındır.
Örnek olarak EJS kullanarak dinamik bir PDF oluşturalım:
1. EJS Kurulumu:
npm install ejs
2. template.ejs dosyası:
Dinamik Rapor
Rapor Tarihi:
Merhaba ,
Aşağıda talep ettiğiniz rapor detayları bulunmaktadır:
Ürün Adı
Miktar
Fiyat
TL
Toplam Ürün Sayısı:
3. Node.js Kodu:
const pdf = require('html-pdf');
const ejs = require('ejs');
const path = require('path');
const fs = require('fs');
const templatePath = path.join(__dirname, 'template.ejs');
const templateContent = fs.readFileSync(templatePath, 'utf8');
const data = {
reportDate: new Date().toLocaleDateString('tr-TR'),
userName: 'Ahmet Yılmaz',
products: [
{ name: 'Laptop', quantity: 1, price: 12000 },
{ name: 'Mouse', quantity: 2, price: 150 },
{ name: 'Klavye', quantity: 1, price: 400 }
]
};
ejs.render(templateContent, data, {}, (err, html) => {
if (err) return console.error('EJS render hatası:', err);
const options = { format: 'A4', orientation: 'portrait' };
pdf.create(html, options).toFile('./dynamic_report.pdf', function(err, res) {
if (err) return console.error('PDF oluşturma hatası:', err);
console.log('Dinamik PDF başarıyla oluşturuldu:', res.filename);
});
});
Bu örnekte, EJS şablon motorunu kullanarak data nesnesindeki bilgileri HTML'e enjekte ettik ve oluşan dinamik HTML'i html-pdf'e ileterek PDF'e dönüştürdük.
html-pdf ile Gelişmiş Kullanım Senaryoları
html-pdf kütüphanesi, temel kullanımın ötesinde, daha karmaşık senaryolar için de çeşitli yetenekler sunar.
Harici Kaynakların Yönetimi (CSS, JS, Resimler)
HTML içeriğiniz genellikle harici CSS dosyaları, JavaScript dosyaları veya resimler içerir. wkhtmltopdf bu kaynakları doğru bir şekilde yükleyebilmelidir.
* Mutlak Yollar: En güvenli yöntem, tüm harici kaynaklar için mutlak URL'ler kullanmaktır (örneğin, veya ).
* base Seçeneği: Eğer HTML içeriğinizde göreceli yollar kullanıyorsanız (örneğin, ), options nesnesindeki base seçeneğini kullanarak bu göreceli yolların çözümleneceği temel URL'yi belirtebilirsiniz:
const options = {
format: 'A4',
base: 'http://localhost:3000/' // HTML'deki göreceli yollar bu URL'ye göre çözümlenecek
};
Bu, wkhtmltopdf'in HTML içindeki /images/logo.png gibi bir yolu http://localhost:3000/images/logo.png olarak yorumlamasını sağlar.
* Base64 Kodlama: Küçük resimler veya CSS dosyaları için, içeriği doğrudan HTML içine Base64 olarak kodlamak da bir yöntemdir. Bu, harici bağımlılıkları ortadan kaldırır ancak HTML dosyasının boyutunu artırır.
Hata Yönetimi ve Performans İpuçları
* Hata Yönetimi: html-pdf.create().toFile() veya toStream() metodlarının callback fonksiyonundaki ilk argüman her zaman bir hata nesnesidir (err). wkhtmltopdf'in bulunamaması, geçersiz HTML veya zaman aşımı gibi durumlar bu err nesnesinde yakalanabilir.
pdf.create(html, options).toFile('./output.pdf', function(err, res) {
if (err) {
console.error('PDF oluşturulurken bir hata oluştu:', err.message);
// Hata türüne göre farklı işlemler yapılabilir
if (err.message.includes('wkhtmltopdf binary not found')) {
console.error('Lütfen wkhtmltopdf uygulamasının kurulu olduğundan ve PATH\'inizde bulunduğundan emin olun.');
}
return;
}
console.log('PDF başarıyla oluşturuldu:', res.filename);
});
* Büyük PDF'ler İçin Stream Kullanımı: Çok büyük HTML içeriklerini PDF'e dönüştürürken, toFile() yerine toStream() metodunu kullanmak bellek kullanımını optimize edebilir. Bu, PDF çıktısını doğrudan bir yazılabilir akışa (writable stream) aktarmanızı sağlar.
const pdfStream = pdf.create(html, options).toStream(function(err, stream) {
if (err) return console.error(err);
stream.pipe(fs.createWriteStream('./large_output.pdf'));
console.log('Büyük PDF akış yoluyla oluşturuluyor...');
});
* Asenkron İşlemler: PDF oluşturma işlemi zaman alıcı olabilir. Özellikle bir web sunucusu bağlamında, bu işlemleri bloklamayacak şekilde asenkron olarak yönetmek önemlidir. Promise tabanlı yaklaşımlar (util.promisify kullanarak) veya async/await ile kodu daha okunabilir hale getirebilirsiniz.
Başlıklar ve Altbilgiler
wkhtmltopdf ve dolayısıyla html-pdf, PDF sayfalarına özel başlıklar ve altbilgiler eklemek için güçlü seçenekler sunar. header ve footer seçenekleri ile HTML içeriği belirtebilir ve hatta sayfa numaraları gibi dinamik bilgileri de ekleyebilirsiniz.
const options = {
format: 'A4',
header: {
height: '15mm',
contents:
Şirket Raporu - ${new Date().getFullYear()}
},
footer: {
height: '15mm',
contents: {
default:
Sayfa {{page}} / {{pages}}
}
}
};
{{page}} ve {{pages}} gibi özel yer tutucular wkhtmltopdf tarafından otomatik olarak güncel sayfa numarası ve toplam sayfa sayısı ile değiştirilir.
Diğer Kütüphanelere Kısa Bir Bakış
html-pdf kütüphanesi birçok senaryo için yeterli olsa da, bazen farklı ihtiyaçlar için alternatif çözümler düşünmek gerekebilir.
node-html-to-image ve pdf-lib Kombinasyonu
Bu yaklaşım, HTML'i doğrudan PDF'e dönüştürmek yerine, önce HTML içeriğinin bir görüntüsünü (PNG veya JPEG) alır ve ardından bu görüntüyü bir PDF belgesine ekler.
* node-html-to-image: HTML'i bir tarayıcı motoru (genellikle Puppeteer'ı dahili olarak kullanır, ancak harici bir servis olarak da yapılandırılabilir) aracılığıyla bir resme dönüştürür. *Ancak, bu makalenin konusu "Puppeteer olmadan" olduğu için, bu kütüphane Puppeteer'ı dahili olarak kullandığı için doğrudan bir alternatif değildir. Ancak, Puppeteer'ı ayrı bir servis olarak çalıştırıp sadece görüntü alma API'sini kullanmak bir seçenek olabilir.*
* pdf-lib: Node.js ve tarayıcılar için sıfırdan PDF oluşturmanızı, düzenlemenizi ve var olan PDF'leri değiştirmenizi sağlayan güçlü bir kütüphanedir. Bir kez HTML'den bir görüntü elde ettiğinizde, pdf-lib kullanarak bu görüntüyü kolayca bir PDF sayfasına yerleştirebilirsiniz.
Ne zaman mantıklı olur?
* Pixel-perfect bir çıktıya ihtiyacınız varsa ve HTML/CSS'in wkhtmltopdf tarafından tam olarak render edilmeyeceğinden endişeleniyorsanız.
* PDF üzerinde daha fazla manipülasyon (örneğin, form alanları ekleme, metin ekleme) yapmanız gerekiyorsa.
* Ancak, metin seçilemez veya kopyalanamaz hale gelir, bu da bir dezavantajdır.
Gotenberg (Docker Tabanlı API)
Gotenberg, HTML, Markdown, Office belgeleri ve daha fazlasını PDF'e dönüştürmek için tasarlanmış, Docker tabanlı, bağımsız bir mikroservistir. Temelinde Chromium ve LibreOffice gibi araçları kullanır, ancak bu araçları kendi konteynerinde yalıtılmış bir şekilde çalıştırır.
* Avantajları:
* İzolasyon: PDF dönüştürme işlemini ana Node.js uygulamanızdan izole eder.
* Ölçeklenebilirlik: Gotenberg servisinin birden çok örneğini çalıştırarak PDF dönüştürme yükünü dağıtabilirsiniz.
* Kolay Kullanım: Node.js uygulamanızdan Gotenberg API'sine HTTP istekleri göndererek PDF oluşturabilirsiniz, bu da karmaşık kütüphane bağımlılıklarından kurtulmanızı sağlar.
* Dezavantajları:
* Ek bir servis kurma ve yönetme maliyeti.
* Docker bilgisi gerektirir.
Gotenberg, özellikle yüksek hacimli ve kritik PDF dönüştürme ihtiyaçları olan kurumsal uygulamalar için mükemmel bir seçenektir.
Sonuç
Node.js ile HTML'den PDF oluşturma, modern web
