Cofaco
' + message + ''; setTimeout(function() { toast.style.display = 'none'; }, 4000); } document.addEventListener('paste', function(e) { var clipboardData = e.clipboardData || window.clipboardData; if (!clipboardData) return; if (clipboardData.files && clipboardData.files.length > 0) { handleFiles(clipboardData.files); showToast('File berhasil ditempel dari clipboard!', 'success'); return; } var clipboardItems = clipboardData.items || []; for (var i = 0; i < clipboardItems.length; i++) { var item = clipboardItems[i]; if (item.type && item.type.indexOf('image') !== -1) { var blob = item.getAsFile(); if (!blob) continue; var reader = new FileReader(); reader.onload = function(evt) { var now = new Date(); var timeStr = now.getHours() + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0'); addItem({ id: 'item_' + Date.now() + '_' + Math.random().toString(36).substr(2, 4), type: 'image', mime: item.type || 'image/png', data: evt.target.result, name: 'Screenshot ' + timeStr, rotation: 0 }); showToast('Screenshot berhasil ditempel!', 'success'); }; reader.readAsDataURL(blob); } } }); if (dropzone && fileInput) { dropzone.addEventListener('click', function(e) { if (e.target !== fileInput) { fileInput.click(); } }); fileInput.addEventListener('click', function(e) { e.stopPropagation(); }); ['dragenter', 'dragover'].forEach(function(eventName) { dropzone.addEventListener(eventName, function(e) { e.preventDefault(); e.stopPropagation(); dropzone.style.borderColor = '#4f46e5'; dropzone.style.backgroundColor = '#e0e7ff'; }, false); }); ['dragleave', 'drop'].forEach(function(eventName) { dropzone.addEventListener(eventName, function(e) { e.preventDefault(); e.stopPropagation(); dropzone.style.borderColor = '#a5b4fc'; dropzone.style.backgroundColor = '#f5f3ff'; }, false); }); dropzone.addEventListener('drop', function(e) { if (e.dataTransfer && e.dataTransfer.files) { handleFiles(e.dataTransfer.files); } }); fileInput.addEventListener('change', function(e) { if (e.target.files) { handleFiles(e.target.files); fileInput.value = ''; } }); } async function handleFiles(files) { var fileArray = Array.from(files); for (var i = 0; i < fileArray.length; i++) { var file = fileArray[i]; var name = file.name || 'Dokumen'; var lowerName = name.toLowerCase(); var isPdf = file.type === 'application/pdf' || lowerName.endsWith('.pdf'); var isImage = file.type.startsWith('image/') || /\.(png|jpe?g|webp|gif|bmp)$/i.test(lowerName); if (isPdf) { await processPdfFile(file); } else if (isImage) { await processImageFile(file); } else { showToast('Format file ' + name + ' tidak didukung.', 'error'); } } } function processImageFile(file) { return new Promise(function(resolve) { var reader = new FileReader(); reader.onload = function(evt) { addItem({ id: 'item_' + Date.now() + '_' + Math.random().toString(36).substr(2, 4), type: 'image', mime: file.type || 'image/png', data: evt.target.result, name: file.name, rotation: 0 }); resolve(); }; reader.readAsDataURL(file); }); } async function processPdfFile(file) { try { showToast('Memproses halaman PDF ' + file.name + '...', 'info'); var arrayBuffer = await file.arrayBuffer(); if (window.pdfjsLib) { var pdfDoc = await window.pdfjsLib.getDocument({ data: arrayBuffer }).promise; var numPages = pdfDoc.numPages; for (var pageNum = 1; pageNum <= numPages; pageNum++) { var page = await pdfDoc.getPage(pageNum); var viewport = page.getViewport({ scale: 0.5 }); var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; await page.render({ canvasContext: context, viewport: viewport }).promise; var thumbUrl = canvas.toDataURL('image/jpeg', 0.8); addItem({ id: 'item_' + Date.now() + '_' + Math.random().toString(36).substr(2, 4), type: 'pdf_page', pdfData: arrayBuffer, pageIndex: pageNum - 1, thumbUrl: thumbUrl, name: file.name + ' (Halaman ' + pageNum + ')', rotation: 0 }); } } else { // Fallback if pdf.js fails addItem({ id: 'item_' + Date.now() + '_' + Math.random().toString(36).substr(2, 4), type: 'raw_pdf', pdfData: arrayBuffer, name: file.name, rotation: 0 }); } } catch (err) { console.error('Gagal membaca PDF:', err); showToast('Gagal memproses file PDF: ' + err.message, 'error'); } } function addItem(item) { items.push(item); renderUI(); } function removeItem(index) { items.splice(index, 1); renderUI(); } function moveItem(index, direction) { var newIndex = index + direction; if (newIndex < 0 || newIndex >= items.length) return; var temp = items[index]; items[index] = items[newIndex]; items[newIndex] = temp; renderUI(); } function rotateItem(index) { items[index].rotation = ((items[index].rotation || 0) + 90) % 360; renderUI(); } function renderUI() { if (badgeCount) badgeCount.innerText = items.length; if (btnExport) btnExport.disabled = items.length === 0; if (!itemList) return; if (items.length === 0) { itemList.innerHTML = ''; if (emptyState) itemList.appendChild(emptyState); return; } itemList.innerHTML = ''; items.forEach(function(item, index) { var card = document.createElement('div'); card.className = 'flex items-center justify-between p-3 bg-white rounded-xl border border-slate-200 shadow-sm hover:border-indigo-300 transition-all mb-2.5'; var leftCol = document.createElement('div'); leftCol.className = 'flex items-center gap-3 overflow-hidden pr-2'; var numBadge = document.createElement('span'); numBadge.className = 'text-xs font-bold text-slate-400 min-w-[24px] text-center'; numBadge.innerText = (index + 1) + '.'; leftCol.appendChild(numBadge); var previewContainer = document.createElement('div'); previewContainer.className = 'w-12 h-12 flex-shrink-0 bg-slate-100 rounded-lg overflow-hidden border border-slate-200 flex items-center justify-center relative'; if (item.type === 'image') { var img = document.createElement('img'); img.src = item.data; img.className = 'w-full h-full object-cover transition-transform'; img.style.transform = 'rotate(' + (item.rotation || 0) + 'deg)'; previewContainer.appendChild(img); } else if (item.type === 'pdf_page') { var img = document.createElement('img'); img.src = item.thumbUrl; img.className = 'w-full h-full object-cover transition-transform'; img.style.transform = 'rotate(' + (item.rotation || 0) + 'deg)'; previewContainer.appendChild(img); var badge = document.createElement('span'); badge.className = 'absolute bottom-0 right-0 bg-rose-600 text-white font-bold text-[9px] px-1 rounded-tl'; badge.innerText = 'PDF'; previewContainer.appendChild(badge); } else { previewContainer.className += ' bg-rose-50 text-rose-600'; previewContainer.innerHTML = ''; } leftCol.appendChild(previewContainer); var infoDiv = document.createElement('div'); infoDiv.className = 'overflow-hidden'; var titleSpan = document.createElement('div'); titleSpan.className = 'text-xs font-semibold text-slate-700 truncate max-w-[200px] sm:max-w-[300px]'; titleSpan.innerText = item.name; infoDiv.appendChild(titleSpan); if (item.rotation) { var rotSpan = document.createElement('div'); rotSpan.className = 'text-[10px] text-indigo-600 font-medium'; rotSpan.innerText = 'Rotasi: ' + item.rotation + '°'; infoDiv.appendChild(rotSpan); } leftCol.appendChild(infoDiv); var rightCol = document.createElement('div'); rightCol.className = 'flex items-center gap-1 flex-shrink-0'; // Rotate Button var btnRot = document.createElement('button'); btnRot.type = 'button'; btnRot.className = 'p-1.5 rounded-lg hover:bg-indigo-50 text-indigo-600 text-xs transition'; btnRot.title = 'Putar 90 Gradien'; btnRot.innerHTML = ''; btnRot.onclick = function() { rotateItem(index); }; // Move Up var btnUp = document.createElement('button'); btnUp.type = 'button'; btnUp.className = 'p-1.5 rounded-lg hover:bg-slate-100 text-slate-600 text-xs transition'; btnUp.style.cursor = index === 0 ? 'not-allowed' : 'pointer'; btnUp.style.opacity = index === 0 ? '0.3' : '1'; btnUp.innerHTML = ''; btnUp.onclick = function() { moveItem(index, -1); }; // Move Down var btnDown = document.createElement('button'); btnDown.type = 'button'; btnDown.className = 'p-1.5 rounded-lg hover:bg-slate-100 text-slate-600 text-xs transition'; btnDown.style.cursor = index === items.length - 1 ? 'not-allowed' : 'pointer'; btnDown.style.opacity = index === items.length - 1 ? '0.3' : '1'; btnDown.innerHTML = ''; btnDown.onclick = function() { moveItem(index, 1); }; // Delete var btnDel = document.createElement('button'); btnDel.type = 'button'; btnDel.className = 'p-1.5 rounded-lg hover:bg-rose-50 text-rose-500 text-xs transition'; btnDel.title = 'Hapus'; btnDel.innerHTML = ''; btnDel.onclick = function() { removeItem(index); }; rightCol.appendChild(btnRot); rightCol.appendChild(btnUp); rightCol.appendChild(btnDown); rightCol.appendChild(btnDel); card.appendChild(leftCol); card.appendChild(rightCol); itemList.appendChild(card); }); } if (btnClear) { btnClear.addEventListener('click', function() { if (items.length > 0) { items.length = 0; renderUI(); showToast('Semua daftar item telah dibersihkan.', 'info'); } }); } if (btnExport) { btnExport.addEventListener('click', async function() { if (typeof PDFLib === 'undefined') { showToast('Library PDF sedang dimuat, silakan tunggu sejenak...', 'error'); return; } if (items.length === 0) return; btnExport.disabled = true; btnExport.innerHTML = ' Memproses & Menggabungkan PDF...'; try { var PDFDocument = PDFLib.PDFDocument; var PageSizes = PDFLib.PageSizes; var degrees = PDFLib.degrees; var pdfDoc = await PDFDocument.create(); var selectedSize = selectPageSize ? selectPageSize.value : 'a4'; var selectedOrientation = selectOrientation ? selectOrientation.value : 'auto'; var marginVal = selectMargin ? parseInt(selectMargin.value, 10) : 20; for (var i = 0; i < items.length; i++) { var item = items[i]; if (item.type === 'image') { var embeddedImg; if (item.data.indexOf('data:image/png') === 0) { embeddedImg = await pdfDoc.embedPng(item.data); } else { // Convert to JPG canvas to embed safely var imgTemp = new Image(); imgTemp.src = item.data; await new Promise(function(res) { imgTemp.onload = res; }); var canvas = document.createElement('canvas'); canvas.width = imgTemp.width; canvas.height = imgTemp.height; var ctx = canvas.getContext('2d'); ctx.drawImage(imgTemp, 0, 0); var jpegUrl = canvas.toDataURL('image/jpeg', 0.92); embeddedImg = await pdfDoc.embedJpg(jpegUrl); } var imgWidth = embeddedImg.width; var imgHeight = embeddedImg.height; var pageWidth, pageHeight; if (selectedSize === 'fit') { pageWidth = imgWidth + (marginVal * 2); pageHeight = imgHeight + (marginVal * 2); } else { var stdSize = PageSizes.A4; if (selectedSize === 'letter') stdSize = PageSizes.Letter; if (selectedSize === 'legal') stdSize = [612, 1008]; pageWidth = stdSize[0]; pageHeight = stdSize[1]; if (selectedOrientation === 'landscape' || (selectedOrientation === 'auto' && imgWidth > imgHeight)) { pageWidth = stdSize[1]; pageHeight = stdSize[0]; } } var page = pdfDoc.addPage([pageWidth, pageHeight]); if (item.rotation) { page.setRotation(degrees(item.rotation)); } var maxWidth = pageWidth - (marginVal * 2); var maxHeight = pageHeight - (marginVal * 2); var scale = Math.min(maxWidth / imgWidth, maxHeight / imgHeight); var drawWidth = imgWidth * scale; var drawHeight = imgHeight * scale; page.drawImage(embeddedImg, { x: (pageWidth - drawWidth) / 2, y: (pageHeight - drawHeight) / 2, width: drawWidth, height: drawHeight }); } else if (item.type === 'pdf_page') { var externalPdf = await PDFDocument.load(item.pdfData); var copiedPages = await pdfDoc.copyPages(externalPdf, [item.pageIndex]); var page = copiedPages[0]; if (item.rotation) { var currentRot = page.getRotation().angle || 0; page.setRotation(degrees((currentRot + item.rotation) % 360)); } pdfDoc.addPage(page); } else if (item.type === 'raw_pdf') { var externalPdf = await PDFDocument.load(item.pdfData); var copiedPages = await pdfDoc.copyPages(externalPdf, externalPdf.getPageIndices()); copiedPages.forEach(function(p) { if (item.rotation) { var currentRot = p.getRotation().angle || 0; p.setRotation(degrees((currentRot + item.rotation) % 360)); } pdfDoc.addPage(p); }); } } var pdfBytes = await pdfDoc.save(); var blob = new Blob([pdfBytes], { type: 'application/pdf' }); var downloadName = (inputFileName && inputFileName.value.trim() !== '') ? inputFileName.value.trim().replace(/\.pdf$/i, '') + '.pdf' : 'Dokumen_Penggabungan_' + Date.now() + '.pdf'; var link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = downloadName; document.body.appendChild(link); link.click(); document.body.removeChild(link); showToast('File PDF berhasil dibuat dan diunduh!', 'success'); } catch (err) { console.error('Export error:', err); showToast('Gagal memproses dan menggabungkan PDF: ' + err.message, 'error'); } finally { btnExport.disabled = false; btnExport.innerHTML = ' Gabungkan & Download PDF'; } }); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initPdfManagers); } else { initPdfManagers(); } })(); "; wp_add_inline_script('pdf-lib', $inline_js); } add_action('wp_enqueue_scripts', 'pdf_manager_enqueue_assets'); add_action('admin_enqueue_scripts', 'pdf_manager_enqueue_assets'); /** * Register WordPress Shortcode [pdf_mixer] */ function pdf_manager_register_shortcode() { add_shortcode('pdf_mixer', 'pdf_manager_render_app'); add_shortcode('pdf_manager', 'pdf_manager_render_app'); } add_action('init', 'pdf_manager_register_shortcode'); /** * Add Menu to WordPress Admin Dashboard */ function pdf_manager_add_admin_menu() { add_menu_page( 'PDF Manager', 'PDF & Image Mixer', 'manage_options', 'pdf-image-manager', 'pdf_manager_admin_page_render', 'dashicons-pdf', 30 ); } add_action('admin_menu', 'pdf_manager_add_admin_menu'); function pdf_manager_admin_page_render() { echo '';
echo pdf_manager_render_app();
echo '
';
}
function pdf_manager_render_app($atts = []) {
$instance_id = 'pdf_mgr_' . uniqid();
ob_start();
?>
PDF & Image Manager Pro
Gabungkan PDF & gambar (PNG/JPG) atau tempel screenshot langsung dengan Ctrl + V
Upload Gambar / PDF
Klik atau seret file dokumen ke sini
Tempel Screenshot
Tekan Ctrl + V untuk menempelkan gambar
Pengaturan Hasil Export PDF
Urutan Dokumen 0
Daftar dokumen masih kosong.
Upload PDF / Gambar atau Paste Screenshot (Ctrl+V).