<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Profesyonel PDF Dönüştürücü</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--primary-color: #4f46e5;
--primary-hover: #4338ca;
--bg-color: #f3f4f6;
--card-bg: #ffffff;
--text-main: #111827;
--text-muted: #6b7280;
--border-color: #e5e7eb;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-color);
color: var(--text-main);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.card {
background-color: var(--card-bg);
width: 100%;
max-width: 550px;
padding: 2rem;
border-radius: 16px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
}
h2 { text-align: center; margin-bottom: 0.5rem; font-weight: 600; }
p.subtitle { text-align: center; color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1.5rem; }
.drop-zone {
border: 2px dashed #cbd5e1;
border-radius: 12px;
padding: 2.5rem 1.5rem;
cursor: pointer;
text-align: center;
transition: all 0.2s ease;
background-color: #f8fafc;
margin-bottom: 1rem;
}
.drop-zone:hover, .drop-zone.dragover { border-color: var(--primary-color); background-color: #eef2ff; }
.drop-zone svg { width: 48px; height: 48px; fill: #94a3b8; margin-bottom: 10px; }
.drop-zone:hover svg, .drop-zone.dragover svg { fill: var(--primary-color); }
.file-list { font-size: 0.85rem; color: var(--text-muted); margin-bottom: 1rem; max-height: 100px; overflow-y: auto; }
.options-panel {
background-color: #f9fafb;
padding: 1rem;
border-radius: 8px;
border: 1px solid var(--border-color);
margin-bottom: 1.5rem;
display: none; /* Dosya seçilene kadar gizli */
}
.option-group { margin-bottom: 1rem; }
.option-group:last-child { margin-bottom: 0; }
.option-group label { font-weight: 500; font-size: 0.9rem; display: block; margin-bottom: 0.5rem; }
select { width: 100%; padding: 0.5rem; border: 1px solid var(--border-color); border-radius: 6px; font-family: inherit; }
.radio-group { display: flex; gap: 1rem; }
.radio-group label { font-weight: 400; display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer; }
.warning-text { font-size: 0.8rem; color: #d97706; margin-top: 0.5rem; display: none; }
.btn {
background-color: var(--primary-color); color: white; border: none; padding: 0.875rem;
width: 100%; border-radius: 8px; font-size: 1rem; font-weight: 500; cursor: pointer; display: none;
}
.btn:hover { background-color: var(--primary-hover); }
.btn:disabled { background-color: #9ca3af; cursor: not-allowed; }
.status-area { margin-top: 1.5rem; display: none; }
.status-text { font-size: 0.875rem; font-weight: 500; margin-bottom: 0.5rem; display: flex; justify-content: space-between; }
.progress-bar-bg { width: 100%; background-color: var(--border-color); border-radius: 999px; height: 8px; overflow: hidden; }
.progress-bar-fill { height: 100%; background-color: var(--primary-color); width: 0%; transition: width 0.3s ease; }
</style>
</head>
<body>
<div class="card">
<h2>PDF'den Resme</h2>
<p class="subtitle">İstediğiniz formatta (PNG, JPG, WebP) dönüştürün ve indirin.</p>
<div class="drop-zone" id="drop-zone">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>
<p><strong>Dosyaları buraya sürükleyin</strong></p>
<p style="font-size: 0.8rem; color: #94a3b8;">veya seçmek için tıklayın</p>
<input type="file" id="file-input" accept="application/pdf" multiple style="display: none;">
</div>
<div class="file-list" id="file-list"></div>
<div class="options-panel" id="options-panel">
<div class="option-group">
<label>Görüntü Formatı:</label>
<select id="image-format">
<option value="jpeg">JPEG (.jpg) - Küçük Boyut</option>
<option value="png" selected>PNG (.png) - Yüksek Kalite</option>
<option value="webp">WebP (.webp) - Modern Web Formatı</option>
</select>
</div>
<div class="option-group">
<label>İndirme Yöntemi:</label>
<div class="radio-group">
<label><input type="radio" name="download-mode" value="zip" checked> Tek Bir ZIP Dosyası</label>
<label><input type="radio" name="download-mode" value="individual"> Ayrı Ayrı İndir</label>
</div>
<div class="warning-text" id="dl-warning">
⚠️ Not: Çok fazla sayfa varsa, tarayıcınız "Çoklu dosya indirme" izni isteyebilir.
</div>
</div>
</div>
<button class="btn" id="convert-btn">Dönüştür ve İndir</button>
<div class="status-area" id="status-area">
<div class="status-text">
<span id="status-message">Hazırlanıyor...</span>
<span id="status-percentage">0%</span>
</div>
<div class="progress-bar-bg">
<div class="progress-bar-fill" id="progress-bar"></div>
</div>
</div>
</div>
<script>
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
const dropZone = document.getElementById('drop-zone');
const fileInput = document.getElementById('file-input');
const fileList = document.getElementById('file-list');
const optionsPanel = document.getElementById('options-panel');
const convertBtn = document.getElementById('convert-btn');
const statusArea = document.getElementById('status-area');
const dlWarning = document.getElementById('dl-warning');
let selectedFiles = [];
// UI Olayları
dropZone.addEventListener('click', () => fileInput.click());
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => dropZone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); }));
['dragenter', 'dragover'].forEach(evt => dropZone.addEventListener(evt, () => dropZone.classList.add('dragover')));
['dragleave', 'drop'].forEach(evt => dropZone.addEventListener(evt, () => dropZone.classList.remove('dragover')));
dropZone.addEventListener('drop', e => handleFiles(e.dataTransfer.files));
fileInput.addEventListener('change', function() { handleFiles(this.files); });
// İndirme yöntemi değiştiğinde uyarıyı göster/gizle
document.querySelectorAll('input[name="download-mode"]').forEach(radio => {
radio.addEventListener('change', (e) => {
dlWarning.style.display = e.target.value === 'individual' ? 'block' : 'none';
});
});
function handleFiles(files) {
const newFiles = Array.from(files).filter(f => f.type === 'application/pdf');
if(newFiles.length === 0) { alert("Lütfen PDF dosyası seçin."); return; }
selectedFiles = newFiles;
fileList.innerHTML = `<strong>Seçilen Dosyalar (${selectedFiles.length}):</strong><br>` + selectedFiles.map(f => `- ${f.name}`).join('<br>');
optionsPanel.style.display = 'block';
convertBtn.style.display = 'block';
statusArea.style.display = 'none';
}
// Ana İşlem Döngüsü
convertBtn.addEventListener('click', async () => {
if (selectedFiles.length === 0) return;
const format = document.getElementById('image-format').value;
const mimeType = `image/${format}`;
const isZipMode = document.querySelector('input[name="download-mode"]:checked').value === 'zip';
const ext = format === 'jpeg' ? 'jpg' : format;
convertBtn.disabled = true;
convertBtn.innerText = "İşleniyor, Lütfen Bekleyin...";
statusArea.style.display = 'block';
let globalZip = isZipMode ? new JSZip() : null;
let totalFiles = selectedFiles.length;
try {
for (let i = 0; i < totalFiles; i++) {
let file = selectedFiles[i];
let arrayBuffer = await file.arrayBuffer();
let pdf = await pdfjsLib.getDocument({data: arrayBuffer}).promise;
let totalPages = pdf.numPages;
for (let pageNum = 1; pageNum <= totalPages; pageNum++) {
let percent = Math.round(((pageNum - 1) / totalPages) * 100);
document.getElementById('status-message').innerText = `Dosya ${i+1}/${totalFiles} - Sayfa ${pageNum}/${totalPages}...`;
document.getElementById('status-percentage').innerText = `${percent}%`;
document.getElementById('progress-bar').style.width = `${percent}%`;
const page = await pdf.getPage(pageNum);
const viewport = page.getViewport({ scale: 2.0 });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
await page.render({ canvasContext: context, viewport: viewport }).promise;
// Canvas'ı resme dönüştür (JPEG için kalite 0.9 olarak ayarlandı)
const imgBlob = await new Promise(resolve => canvas.toBlob(resolve, mimeType, 0.9));
const fileName = `${file.name.replace('.pdf', '')}_S${pageNum}.${ext}`;
if (isZipMode) {
globalZip.file(fileName, imgBlob);
} else {
saveAs(imgBlob, fileName);
// Tarayıcının çoklu indirme engeline takılmamak için araya küçük bir gecikme ekliyoruz
await new Promise(r => setTimeout(r, 300));
}
}
}
if (isZipMode) {
document.getElementById('status-message').innerText = `Tüm dosyalar ZIP'leniyor...`;
document.getElementById('progress-bar').style.width = `100%`;
document.getElementById('status-percentage').innerText = `100%`;
const content = await globalZip.generateAsync({type: "blob"});
saveAs(content, `PDF_Resimleri_Toplu.zip`);
}
document.getElementById('status-message').innerText = "İşlem Başarılı!";
document.getElementById('progress-bar').style.backgroundColor = "#10b981";
} catch (error) {
console.error(error);
document.getElementById('status-message').innerText = "Hata Oluştu!";
document.getElementById('progress-bar').style.backgroundColor = "#ef4444";
} finally {
convertBtn.disabled = false;
convertBtn.innerText = "Yeni Dosyaları Dönüştür";
}
});
</script>
</body>
</html>