PDFIX / split.html
Talha912's picture
Update split.html
525b057 verified
Raw History Blame Contribute Delete
17.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#f7f8ff" />
<meta name="description" content="Split PDF files and extract selected pages in your browser with PDFix." />
<title>Split PDF Files Online — PDFix</title>
<style>
:root {
--bg:#f7f8ff; --ink:#17182d; --muted:#71738a; --line:#e7e8f2;
--primary:#635bff; --primary-dark:#4c43e8; --green:#119b82; --danger:#d94765;
--shadow:0 18px 55px rgba(47,43,112,.09);
}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;color:var(--ink);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;line-height:1.5;background:radial-gradient(circle at 85% 5%,rgba(177,171,255,.19),transparent 25rem),radial-gradient(circle at 4% 35%,rgba(115,221,211,.12),transparent 22rem),var(--bg)}
a{color:inherit;text-decoration:none} button,input{font:inherit} button{cursor:pointer}
.container{width:min(1040px,calc(100% - 32px));margin:0 auto}
header{padding:20px 0}.nav{display:flex;align-items:center;justify-content:space-between;gap:18px}
.brand{display:inline-flex;align-items:center;gap:10px;font-size:1.4rem;font-weight:850;letter-spacing:-1px}
.brand-mark{width:39px;height:39px;display:grid;place-items:center;border-radius:13px;color:#fff;background:linear-gradient(145deg,#766dff,#4c43e8);box-shadow:0 8px 20px rgba(99,91,255,.25)}
.brand-mark svg{width:23px;height:23px}.brand-name span{color:var(--primary)}
.back-link{color:#65677d;font-weight:650;font-size:.92rem}.back-link:hover{color:var(--primary)}
main{padding:42px 0 70px}.intro{text-align:center;max-width:700px;margin:0 auto 28px}
.eyebrow{display:inline-flex;gap:8px;align-items:center;padding:7px 12px;border:1px solid #e4e1ff;border-radius:99px;background:rgba(255,255,255,.8);color:#5a52e9;font-size:.82rem;font-weight:750}
h1{margin:18px 0 12px;font-size:clamp(2.4rem,5vw,4rem);line-height:1.06;letter-spacing:-2.7px}h1 span{color:var(--primary)}
.intro p{margin:0;color:var(--muted);font-size:1.05rem}
.workspace{max-width:820px;margin:0 auto;border:1px solid rgba(99,91,255,.14);border-radius:24px;padding:12px;background:rgba(255,255,255,.9);box-shadow:var(--shadow)}
.dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:210px;padding:26px 18px;text-align:center;border:2px dashed #cbc8ff;border-radius:17px;background:linear-gradient(135deg,rgba(238,237,255,.72),rgba(239,253,251,.5));transition:.2s ease}
.dropzone.dragover{border-color:var(--primary);background:#efedff;transform:scale(.995)}
.upload-icon{width:54px;height:54px;display:grid;place-items:center;margin-bottom:13px;border-radius:17px;color:var(--primary);background:#fff;box-shadow:0 7px 18px rgba(71,64,160,.1)}
.upload-icon svg{width:28px;height:28px}.dropzone strong{font-size:1.1rem}.dropzone p{margin:5px 0 16px;color:var(--muted);font-size:.9rem}
.btn{border:0;display:inline-flex;align-items:center;justify-content:center;gap:9px;padding:12px 18px;border-radius:13px;font-weight:750;transition:.2s ease}
.btn-primary{color:#fff;background:var(--primary);box-shadow:0 8px 18px rgba(99,91,255,.22)}.btn-primary:hover:not(:disabled){background:var(--primary-dark);transform:translateY(-1px)}
.btn-light{color:#4b4d64;background:#fff;border:1px solid var(--line)}.btn-light:hover{border-color:#c9c5ff;color:var(--primary)}
button:disabled{opacity:.5;cursor:not-allowed;box-shadow:none}input[type=file]{display:none}
.file-panel{display:none;padding:22px 10px 8px}.file-panel.visible{display:block}
.file-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;padding:13px;border:1px solid var(--line);border-radius:14px;background:#fff}
.file-icon{width:42px;height:46px;display:grid;place-items:center;border-radius:11px;color:#e6536a;background:#fff0f2}.file-icon svg{width:23px;height:23px}
.file-meta{min-width:0}.file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:700;font-size:.92rem}.file-size{color:var(--muted);font-size:.8rem;margin-top:3px}
.controls{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:20px}
.field{padding:16px;border:1px solid var(--line);border-radius:15px;background:#fff}.field label{display:block;margin-bottom:8px;font-size:.9rem;font-weight:750}
.field input,.field select{width:100%;border:1px solid #dfe0ec;border-radius:10px;padding:11px 12px;outline:none;background:#fff;color:var(--ink)}
.field input:focus,.field select:focus{border-color:#aaa5ff;box-shadow:0 0 0 3px rgba(99,91,255,.1)}
.hint{margin:7px 0 0;color:var(--muted);font-size:.8rem}
.panel-actions{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-top:17px}
.privacy{display:flex;align-items:center;gap:7px;color:var(--muted);font-size:.82rem}.privacy svg{width:17px;height:17px;color:var(--green);flex-shrink:0}
.status{display:none;margin-top:17px;padding:13px 15px;border-radius:12px;font-size:.9rem}.status.show{display:block}.status.info{background:#efedff;color:#4c43c9}.status.success{background:#e6fbf3;color:#087d68}.status.error{background:#fff0f2;color:#b52b49}
.tips{display:grid;grid-template-columns:repeat(3,1fr);gap:15px;max-width:820px;margin:24px auto 0}
.tip{padding:18px;border:1px solid var(--line);border-radius:17px;background:rgba(255,255,255,.72)}.tip b{display:block;margin-bottom:5px;font-size:.92rem}.tip p{margin:0;color:var(--muted);font-size:.84rem}
footer{border-top:1px solid var(--line);padding:24px 0;color:var(--muted);font-size:.85rem}.footer-inner{display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap}
.toast{position:fixed;left:50%;bottom:22px;transform:translate(-50%,18px);opacity:0;pointer-events:none;padding:12px 17px;border-radius:13px;background:#20213a;color:#fff;box-shadow:var(--shadow);z-index:10;transition:.22s ease;max-width:calc(100% - 28px);text-align:center;font-size:.9rem}.toast.show{opacity:1;transform:translate(-50%,0)}
@media(max-width:650px){main{padding-top:30px}h1{letter-spacing:-1.8px}.tips{grid-template-columns:1fr}.controls{grid-template-columns:1fr}.file-row{grid-template-columns:auto minmax(0,1fr)}.panel-actions{align-items:stretch;flex-direction:column}.panel-actions .btn{width:100%}}
</style>
</head>
<body>
<header><div class="container nav">
<a class="brand" href="index.html" aria-label="PDFix homepage">
<span class="brand-mark"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M7 3.5h7l4 4V20a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 6 20V5A1.5 1.5 0 0 1 7.5 3.5Z" stroke="currentColor" stroke-width="1.8"/><path d="M14 3.8V8h4M8.5 12h7M8.5 15.5h7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></span>
<span class="brand-name">PDF<span>ix</span></span>
</a>
<a class="back-link" href="index.html">← Back to all tools</a>
</div></header>
<main><div class="container">
<div class="intro">
<div class="eyebrow">✦ PDFix PDF tools</div>
<h1>Split <span>PDF files</span></h1>
<p>Extract the pages you need from a PDF. Enter page numbers or a range, then download your selected pages as a new PDF.</p>
</div>
<div class="workspace">
<div class="dropzone" id="dropzone" tabindex="0" role="button" aria-label="Choose or drop a PDF file">
<div class="upload-icon"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M12 16V4m0 0L7.5 8.5M12 4l4.5 4.5M5 14.5v4A1.5 1.5 0 0 0 6.5 20h11a1.5 1.5 0 0 0 1.5-1.5v-4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<strong>Drop one PDF file here</strong>
<p>Select a PDF to extract specific pages.</p>
<button type="button" class="btn btn-primary" id="chooseFile">Select PDF file <span aria-hidden="true">↑</span></button>
<input id="fileInput" type="file" accept=".pdf,application/pdf" />
</div>
<div class="file-panel" id="filePanel">
<div class="file-row">
<div class="file-icon"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M6.5 3.5h8l4 4V20a1.5 1.5 0 0 1-1.5 1.5h-10A1.5 1.5 0 0 1 5.5 20V5A1.5 1.5 0 0 1 7 3.5Z" stroke="currentColor" stroke-width="1.7"/><path d="M14.5 3.8V8h4M8 12h8M8 15h6" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg></div>
<div class="file-meta"><div class="file-name" id="fileName">document.pdf</div><div class="file-size" id="fileSize"></div></div>
<button class="btn btn-light" id="changeFile" type="button">Change</button>
</div>
<div class="controls">
<div class="field">
<label for="pageRange">Pages to extract</label>
<input id="pageRange" type="text" placeholder="e.g. 1-3, 5, 8-10" autocomplete="off" />
<p class="hint">Use commas for separate pages and hyphens for ranges.</p>
</div>
<div class="field">
<label for="splitMode">Output mode</label>
<select id="splitMode">
<option value="selected">One PDF with selected pages</option>
<option value="each">Separate PDF for each selected page</option>
</select>
<p class="hint">For separate files, each selected page downloads individually.</p>
</div>
</div>
<div class="panel-actions">
<div class="privacy"><svg viewBox="0 0 24 24" fill="none"><path d="M12 3 19 6v5c0 4.5-2.9 7.7-7 10-4.1-2.3-7-5.5-7-10V6l7-3Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="m9 12 2 2 4-4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg> Your PDF is processed in your browser.</div>
<button type="button" class="btn btn-primary" id="splitBtn" disabled>Split PDF <span aria-hidden="true">→</span></button>
</div>
<div class="status" id="status" role="status" aria-live="polite"></div>
</div>
</div>
<div class="tips">
<article class="tip"><b>① Choose a PDF</b><p>Select the document whose pages you want to extract.</p></article>
<article class="tip"><b>② Enter page numbers</b><p>For example, type 1-3, 5 to select pages 1, 2, 3, and 5.</p></article>
<article class="tip"><b>③ Download your pages</b><p>Save the extracted pages as one PDF or as separate files.</p></article>
</div>
</div></main>
<footer><div class="container footer-inner"><a class="brand" href="index.html"><span class="brand-name">PDF<span>ix</span></span></a><span>© <span id="year"></span> PDFix · Your documents, made simple.</span></div></footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
<script>
const fileInput = document.getElementById('fileInput');
const dropzone = document.getElementById('dropzone');
const filePanel = document.getElementById('filePanel');
const pageRange = document.getElementById('pageRange');
const splitBtn = document.getElementById('splitBtn');
const statusBox = document.getElementById('status');
const toast = document.getElementById('toast');
let selectedFile = null;
let toastTimer;
document.getElementById('year').textContent = new Date().getFullYear();
function showToast(message) {
toast.textContent = message;
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toast.classList.remove('show'), 3000);
}
function setStatus(message, type='info') {
statusBox.textContent = message;
statusBox.className = `status show ${type}`;
}
function clearStatus() {
statusBox.textContent = '';
statusBox.className = 'status';
}
function formatSize(bytes) {
return bytes < 1024*1024 ? `${Math.max(1,Math.round(bytes/1024))} KB` : `${(bytes/(1024*1024)).toFixed(2)} MB`;
}
function choosePdf(files) {
const file = Array.from(files || []).find(f => f.name.toLowerCase().endsWith('.pdf') || f.type === 'application/pdf');
if (!file) { showToast('Please select a PDF file.'); return; }
selectedFile = file;
document.getElementById('fileName').textContent = file.name;
document.getElementById('fileName').title = file.name;
document.getElementById('fileSize').textContent = formatSize(file.size);
filePanel.style.display = 'block';
filePanel.classList.add('visible');
splitBtn.disabled = false;
clearStatus();
pageRange.value = '';
}
function parsePageRange(input, pageCount) {
const text = input.trim();
if (!text) throw new Error('Enter the page numbers or range you want to extract.');
const pages = new Set();
const parts = text.split(',');
for (const rawPart of parts) {
const part = rawPart.trim();
if (!part) throw new Error('Check the page range format. Example: 1-3, 5, 8-10.');
if (/^\d+$/.test(part)) {
const page = Number(part);
if (page < 1 || page > pageCount) throw new Error(`Page ${page} is outside this PDF. It has ${pageCount} pages.`);
pages.add(page - 1);
} else {
const match = part.match(/^(\d+)\s*-\s*(\d+)$/);
if (!match) throw new Error('Invalid page range. Use a format such as 1-3, 5, 8-10.');
const start = Number(match[1]);
const end = Number(match[2]);
if (start < 1 || end < 1 || start > end || end > pageCount) {
throw new Error(`Invalid range "${part}". Use page numbers from 1 to ${pageCount}.`);
}
for (let page = start; page <= end; page++) pages.add(page - 1);
}
}
return Array.from(pages).sort((a,b) => a-b);
}
function downloadBytes(bytes, filename) {
const blob = new Blob([bytes], {type:'application/pdf'});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
link.remove();
setTimeout(() => URL.revokeObjectURL(url), 1500);
}
function safeBaseName(name) {
return name.replace(/\.pdf$/i,'').replace(/[^\w\-]+/g,'_').slice(0,70) || 'document';
}
document.getElementById('chooseFile').addEventListener('click', e => { e.stopPropagation(); fileInput.click(); });
document.getElementById('changeFile').addEventListener('click', () => fileInput.click());
dropzone.addEventListener('click', e => { if (!e.target.closest('button')) fileInput.click(); });
dropzone.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); fileInput.click(); }
});
fileInput.addEventListener('change', () => { choosePdf(fileInput.files); fileInput.value = ''; });
['dragenter','dragover'].forEach(type => dropzone.addEventListener(type, e => {
e.preventDefault(); dropzone.classList.add('dragover');
}));
['dragleave','drop'].forEach(type => dropzone.addEventListener(type, e => {
e.preventDefault(); dropzone.classList.remove('dragover');
}));
dropzone.addEventListener('drop', e => choosePdf(e.dataTransfer.files));
splitBtn.addEventListener('click', async () => {
if (!selectedFile) return;
if (!window.PDFLib) {
setStatus('The PDF library could not load. Check your internet connection and reload this page.', 'error');
return;
}
const originalText = splitBtn.innerHTML;
splitBtn.disabled = true;
splitBtn.textContent = 'Processing…';
try {
setStatus('Reading your PDF…', 'info');
const {PDFDocument} = window.PDFLib;
const source = await PDFDocument.load(await selectedFile.arrayBuffer());
const pageCount = source.getPageCount();
const pages = parsePageRange(pageRange.value, pageCount);
const mode = document.getElementById('splitMode').value;
const base = safeBaseName(selectedFile.name);
if (mode === 'selected') {
const output = await PDFDocument.create();
const copied = await output.copyPages(source, pages);
copied.forEach(page => output.addPage(page));
const bytes = await output.save();
downloadBytes(bytes, `${base}-split.pdf`);
setStatus(`Done! Extracted ${pages.length} ${pages.length === 1 ? 'page' : 'pages'} from a ${pageCount}-page PDF. Your download should begin shortly.`, 'success');
} else {
for (let i = 0; i < pages.length; i++) {
setStatus(`Creating PDF ${i+1} of ${pages.length}…`, 'info');
const output = await PDFDocument.create();
const [page] = await output.copyPages(source, [pages[i]]);
output.addPage(page);
const bytes = await output.save();
downloadBytes(bytes, `${base}-page-${pages[i]+1}.pdf`);
// A small pause helps browsers start multiple downloads.
await new Promise(resolve => setTimeout(resolve, 250));
}
setStatus(`Done! Created ${pages.length} separate PDF file(s). Your browser may ask you to allow multiple downloads.`, 'success');
}
} catch (error) {
console.error(error);
setStatus(error.message || 'Could not split this PDF. It may be damaged or password-protected.', 'error');
} finally {
splitBtn.disabled = !selectedFile;
splitBtn.innerHTML = originalText;
}
});
</script>
</body>
</html>