Every tool you need to work with PDFs in one place

Completely free, secure, and easy to use. Merge, split, compress, convert, rotate, unlock, and watermark PDFs with just a few clicks, directly in your browser.

Advertisement (728x90)

Our Suite of PDF Tools

Select a tool to get started. All processing is done securely on your device.

About One Page Tools

Digital One Plus Tool was born from a simple idea: powerful PDF utilities should be accessible to everyone, for free, without compromising on privacy or security. In today's digital world, PDFs are ubiquitous, but managing them can be a hassle. We believe you shouldn't have to download bulky software or upload your sensitive documents to unknown servers just to perform a simple task.

Our entire suite of tools runs directly in your web browser. This means your files never leave your computer, giving you complete peace of mind. Whether you're a student, a professional, or just someone who needs to quickly manage a document, our tools are designed to be intuitive, fast, and reliable.

Why Are PDF Tools So Essential?

The PDF format is designed to be a universal standard for document sharing, preserving formatting across different devices and operating systems. However, this rigidity can also make them difficult to work with. Our tools bridge that gap:

  • Management: Combine multiple reports into a single file with our Merge tool, or extract specific pages from a large document using the Split tool.
  • Optimization: Large PDF files can be difficult to email or store. Our Compress tool reduces file size without a significant loss in quality.
  • Conversion: Need to edit the text of a PDF? Convert it to Word. Need to create a presentation from a report? Convert PDF to PowerPoint. Our conversion tools make your content more flexible.
  • Editing & Security: Make quick annotations, add a signature, or protect your document with a watermarkβ€”all without leaving your browser.

Our Commitment to Privacy

We can't stress this enough: your files are yours. We do not and cannot access, view, or store the documents you process with our tools. The magic happens entirely on your machine thanks to modern web technologies like WebAssembly and JavaScript. This client-side approach is the core of our philosophy.

Processing...

`; }, process: async (files) => { const quality = parseFloat(document.getElementById('quality-slider').value); const file = files[0]; const pdfBytes = await file.arrayBuffer(); const newPdfDoc = await PDFDocument.create(); const pdf = await pdfjsLib.getDocument({ data: pdfBytes }).promise; const numPages = pdf.numPages; for (let i = 1; i <= numPages; i++) { showLoader(`Processing page ${i}/${numPages}...`); const page = await pdf.getPage(i); const viewport = page.getViewport({ scale: 1.5 }); 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; const jpegDataUrl = canvas.toDataURL('image/jpeg', quality); const jpegImageBytes = await fetch(jpegDataUrl).then(res => res.arrayBuffer()); const jpegImage = await newPdfDoc.embedJpg(jpegImageBytes); const newPage = newPdfDoc.addPage([jpegImage.width, jpegImage.height]); newPage.drawImage(jpegImage, { x: 0, y: 0, width: newPage.getWidth(), height: newPage.getHeight(), }); } showLoader('Finalizing compressed PDF...'); const compressedPdfBytes = await newPdfDoc.save(); createDownloadLink(compressedPdfBytes, 'compressed.pdf', 'application/pdf'); } }, 'pdf-to-word': { title: 'PDF to Word', desc: 'Convert PDF to an editable DOCX file.', icon: 'πŸ“„', fileType: '.pdf', multiple: false, process: async (files) => { showLoader('Extracting text from PDF...'); const file = files[0]; const pdfBytes = await file.arrayBuffer(); const pdf = await pdfjsLib.getDocument({ data: pdfBytes }).promise; let fullText = 'Note: This conversion extracts text only. Formatting is not preserved.\n\n'; for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const textContent = await page.getTextContent(); fullText += textContent.items.map(item => item.str).join(' ') + '\n\n'; } // Since direct DOCX generation is complex, we provide a TXT file as a practical alternative createDownloadLink(new Blob([fullText], {type: 'text/plain'}), 'converted.txt', 'text/plain'); } }, 'pdf-to-powerpoint': { title: 'PDF to PowerPoint', desc: 'Convert each page of a PDF to a PPTX slide.', icon: 'πŸ“Š', fileType: '.pdf', multiple: false, process: async(files) => { const file = files[0]; const pdfBytes = await file.arrayBuffer(); const pdf = await pdfjsLib.getDocument({ data: pdfBytes }).promise; const numPages = pdf.numPages; let pptx = new PptxGenJS(); for(let i = 1; i <= numPages; i++) { showLoader(`Converting page ${i}/${numPages} to slide...`); const page = await pdf.getPage(i); const viewport = page.getViewport({ scale: 2 }); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; await page.render({ canvasContext: context, viewport }).promise; const dataUrl = canvas.toDataURL('image/png'); let slide = pptx.addSlide(); slide.addImage({ data: dataUrl, w: '100%', h: '100%'}); } showLoader('Generating PowerPoint file...'); pptx.writeFile({ fileName: 'converted.pptx' }); // No download link needed, pptxgenjs handles it. } }, 'word-to-pdf': { title: 'Word to PDF', desc: 'Convert DOCX files to PDF.', icon: 'πŸ“œ', fileType: '.docx', multiple: false, process: async (files) => { showLoader('Converting Word to PDF...'); const file = files[0]; const arrayBuffer = await file.arrayBuffer(); const result = await mammoth.convertToHtml({ arrayBuffer: arrayBuffer }); const html = result.value; const element = document.createElement('div'); element.innerHTML = html; // Style it for better PDF output const style = document.createElement('style'); style.innerHTML = `body { font-family: 'Times New Roman', serif; }`; element.prepend(style); html2pdf().from(element).save('word.pdf'); } }, 'edit-pdf': { title: 'Edit PDF', desc: 'Add text, images, and shapes to a PDF file.', icon: '✏️', fileType: '.pdf', multiple: false, options: () => { return `
`; }, onFileSelect: async () => { showLoader('Loading PDF for editing...'); document.getElementById('edit-controls').style.display = 'flex'; const file = selectedFiles[0]; const pdfBytes = await file.arrayBuffer(); pdfDoc = await pdfjsLib.getDocument({ data: pdfBytes }).promise; totalPages = pdfDoc.numPages; currentPageNum = 1; pageRenderData = {}; // Reset edits await renderPdfPageForEditing(currentPageNum); hideLoader(); }, process: async (files) => { showLoader('Applying edits to PDF...'); // Save final page's edits if (fabricCanvas) { pageRenderData[currentPageNum] = fabricCanvas.toJSON(); } const originalPdfBytes = await files[0].arrayBuffer(); const pdf = await PDFDocument.load(originalPdfBytes); for (const pageNumStr in pageRenderData) { const pageNum = parseInt(pageNumStr, 10); const pageData = pageRenderData[pageNum]; if (!pageData || !pageData.objects || pageData.objects.length === 0) continue; showLoader(`Applying edits to page ${pageNum}/${totalPages}...`); const page = pdf.getPages()[pageNum - 1]; const { width, height } = page.getSize(); // Create a temporary canvas to render edits const tempCanvas = document.createElement('canvas'); const tempFabric = new fabric.Canvas(tempCanvas, { width: width, height: height }); tempFabric.loadFromJSON(pageData, async () => { const dataUrl = tempFabric.toDataURL({ format: 'png' }); const pngImageBytes = await fetch(dataUrl).then(res => res.arrayBuffer()); const pngImage = await pdf.embedPng(pngImageBytes); page.drawImage(pngImage, { x: 0, y: 0, width: width, height: height, }); }); } showLoader('Saving edited PDF...'); // This is a simplified approach. For perfect overlay, a delay or promise chain is needed. // We'll add a small delay to allow fabric rendering to complete before saving. setTimeout(async () => { const editedPdfBytes = await pdf.save(); createDownloadLink(editedPdfBytes, 'edited.pdf', 'application/pdf'); hideLoader(); }, 2000); } }, 'sign-pdf': { title: 'Sign PDF', desc: 'Sign documents with your own electronic signature.', icon: 'βœ’οΈ', fileType: '.pdf', multiple: false, options: () => { return `

`; }, onFileSelect: () => { // Initialize signature pad after options are rendered setTimeout(() => { const canvasEl = document.getElementById('signature-canvas'); fabricCanvas = new fabric.Canvas(canvasEl, { isDrawingMode: true, }); fabricCanvas.freeDrawingBrush.width = 3; fabricCanvas.freeDrawingBrush.color = '#000000'; document.getElementById('clear-signature-btn').addEventListener('click', () => fabricCanvas.clear()); }, 100); }, process: async (files) => { showLoader('Adding signature to PDF...'); const signatureDataUrl = fabricCanvas.toDataURL({ format: 'png'}); if (fabricCanvas.isEmpty()) { showError("Please draw a signature first."); return; } const file = files[0]; const pdfBytes = await file.arrayBuffer(); const pdf = await PDFDocument.load(pdfBytes); const signatureImageBytes = await fetch(signatureDataUrl).then(res => res.arrayBuffer()); const signatureImage = await pdf.embedPng(signatureImageBytes); const firstPage = pdf.getPages()[0]; const { width, height } = firstPage.getSize(); firstPage.drawImage(signatureImage, { x: width - signatureImage.width * 0.5 - 60, // Position at bottom-right y: 60, width: signatureImage.width * 0.5, height: signatureImage.height * 0.5, }); const signedPdfBytes = await pdf.save(); createDownloadLink(signedPdfBytes, 'signed.pdf', 'application/pdf'); } }, 'watermark-pdf': { title: 'Watermark PDF', desc: 'Add a text watermark to your PDF file.', icon: 'πŸ’§', fileType: '.pdf', multiple: false, options: () => { return `
`; }, process: async (files) => { showLoader('Adding watermark...'); const text = document.getElementById('watermark-text').value; const opacity = parseFloat(document.getElementById('opacity-slider').value); const file = files[0]; const pdfBytes = await file.arrayBuffer(); const pdf = await PDFDocument.load(pdfBytes); const pages = pdf.getPages(); for (const page of pages) { const { width, height } = page.getSize(); page.drawText(text, { x: width / 2 - 150, y: height / 2, size: 50, color: rgb(0.5, 0.5, 0.5), opacity: opacity, rotate: degrees(-45) }); } const watermarkedPdfBytes = await pdf.save(); createDownloadLink(watermarkedPdfBytes, 'watermarked.pdf', 'application/pdf'); } }, 'rotate-pdf': { title: 'Rotate PDF', desc: 'Rotate all pages of a PDF file.', icon: 'πŸ”„', fileType: '.pdf', multiple: false, options: () => { return `
`; }, process: async (files) => { showLoader('Rotating PDF...'); const angle = parseInt(document.getElementById('rotation-angle').value, 10); const file = files[0]; const pdfBytes = await file.arrayBuffer(); const pdf = await PDFDocument.load(pdfBytes); pdf.getPages().forEach(page => { const currentRotation = page.getRotation().angle; page.setRotation(degrees(currentRotation + angle)); }); const rotatedPdfBytes = await pdf.save(); createDownloadLink(rotatedPdfBytes, 'rotated.pdf', 'application/pdf'); } }, // --- Stubs for other tools --- 'pdf-to-jpg': { title: 'PDF to JPG', desc: 'Convert each PDF page into a JPG image.', icon: 'πŸ–ΌοΈ', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'jpg-to-pdf': { title: 'JPG to PDF', desc: 'Convert JPG images to a single PDF file.', icon: 'πŸ–ΌοΈ', fileType: '.jpg,.jpeg', multiple: true, process: async () => { showError('This tool is coming soon!'); } }, 'excel-to-pdf': { title: 'Excel to PDF', desc: 'Convert Excel spreadsheets to PDF.', icon: 'πŸ“ˆ', fileType: '.xls,.xlsx', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'powerpoint-to-pdf': { title: 'PowerPoint to PDF', desc: 'Convert PPTX presentations to PDF.', icon: 'πŸ“Š', fileType: '.pptx', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'unlock-pdf': { title: 'Unlock PDF', desc: 'Remove password and restrictions from a PDF.', icon: 'πŸ”“', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'protect-pdf': { title: 'Protect PDF', desc: 'Add a password to protect your PDF file.', icon: 'πŸ”’', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'organize-pdf': { title: 'Organize PDF', desc: 'Reorder, add, or remove pages from a PDF.', icon: 'πŸ—‚οΈ', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'pdf-to-pdfa': { title: 'PDF to PDF/A', desc: 'Convert PDF to the PDF/A archival format.', icon: 'πŸ›οΈ', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'repair-pdf': { title: 'Repair PDF', desc: 'Attempt to repair a damaged or corrupt PDF.', icon: 'πŸ› οΈ', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'page-numbers': { title: 'Page Numbers', desc: 'Add page numbers to your PDF file.', icon: '#️⃣', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'ocr-pdf': { title: 'OCR PDF', desc: 'Make scanned PDFs searchable and selectable.', icon: 'πŸ”', fileType: '.pdf', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'html-to-pdf': { title: 'HTML to PDF', desc: 'Convert web pages to PDF documents.', icon: '🌐', fileType: '.html', multiple: false, process: async () => { showError('This tool is coming soon!'); } }, 'png-to-pdf': { title: 'PNG to PDF', desc: 'Convert PNG images to a single PDF.', icon: 'πŸ–ΌοΈ', multiple: true, fileType: '.png', process: async () => { showError('This tool is coming soon!'); }}, 'gif-to-pdf': { title: 'GIF to PDF', desc: 'Convert GIF images to a PDF document.', icon: '🎞️', multiple: true, fileType: '.gif', process: async () => { showError('This tool is coming soon!'); }}, 'tiff-to-pdf': { title: 'TIFF to PDF', desc: 'Convert TIFF images to a PDF file.', icon: 'πŸ“œ', multiple: true, fileType: '.tiff,.tif', process: async () => { showError('This tool is coming soon!'); }}, 'pdf-to-excel': { title: 'PDF to Excel', desc: 'Extract data from PDF tables to Excel.', icon: 'πŸ“ˆ', multiple: false, fileType: '.pdf', process: async () => { showError('This tool is coming soon!'); }}, 'pdf-to-text': { title: 'PDF to Text', desc: 'Extract plain text content from your PDF.', icon: 'πŸ“', multiple: false, fileType: '.pdf', process: async () => { showError('This tool is coming soon!'); }}, }; // --- UI INITIALIZATION & EVENT LISTENERS --- // Generate tool cards on page load function generateToolCards() { Object.keys(toolImplementations).forEach(key => { const tool = toolImplementations[key]; const card = document.createElement('div'); card.className = 'tool-card reveal'; card.dataset.toolId = key; card.innerHTML = `
${tool.icon}

${tool.title}

${tool.desc}

${key === 'edit-pdf' ? 'New!' : ''} `; card.addEventListener('click', () => openModal(key)); toolGrid.appendChild(card); }); } // Header scroll effect window.addEventListener('scroll', () => { if (window.scrollY > 50) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } }); // Hamburger menu toggle hamburger.addEventListener('click', () => { hamburger.classList.toggle('active'); mobileNav.classList.toggle('active'); document.body.style.overflow = mobileNav.classList.contains('active') ? 'hidden' : 'auto'; }); // Close mobile nav when a link is clicked document.querySelectorAll('.mobile-nav .nav-link').forEach(link => { link.addEventListener('click', () => { hamburger.classList.remove('active'); mobileNav.classList.remove('active'); document.body.style.overflow = 'auto'; }); }); // Reveal on scroll animation const revealElements = document.querySelectorAll('.reveal'); const revealObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('active'); revealObserver.unobserve(entry.target); } }); }, { threshold: 0.1 }); revealElements.forEach(el => revealObserver.observe(el)); // --- MODAL & FILE HANDLING LOGIC --- function openModal(toolId) { currentToolId = toolId; const tool = toolImplementations[toolId]; resetModal(); modalTitle.textContent = tool.title; fileInput.accept = tool.fileType; fileInput.multiple = tool.multiple || false; if (tool.options) { modalOptions.innerHTML = tool.options(); modalOptionsContainer.style.display = 'block'; } else { modalOptionsContainer.style.display = 'none'; } modal.classList.add('active'); document.body.style.overflow = 'hidden'; } function closeModal() { modal.classList.remove('active'); document.body.style.overflow = 'auto'; resetModal(); } function resetModal() { selectedFiles = []; currentToolId = null; updateFileList(); processBtn.disabled = true; modalOptions.innerHTML = ''; outputArea.innerHTML = ''; fileInput.value = ''; // Reset file input // Clean up fabric canvas if it exists if (fabricCanvas) { fabricCanvas.dispose(); fabricCanvas = null; } pdfDoc = null; pageRenderData = {}; currentPageNum = 1; totalPages = 0; const editControls = document.getElementById('edit-controls'); if (editControls) editControls.style.display = 'none'; } function handleFiles(files) { const tool = toolImplementations[currentToolId]; const newFiles = Array.from(files); if (!tool.multiple && newFiles.length > 1) { showError(`This tool only accepts a single file.`); return; } if (!tool.multiple) { selectedFiles = []; } const allowedTypes = tool.fileType.split(',').map(t => t.trim()); const validFiles = newFiles.filter(file => allowedTypes.some(type => file.name.toLowerCase().endsWith(type)) ); if (validFiles.length !== newFiles.length) { showError(`Invalid file type. Please select ${tool.fileType} files.`); } selectedFiles.push(...validFiles); updateFileList(); if (selectedFiles.length > 0) { processBtn.disabled = false; if (tool.onFileSelect) { tool.onFileSelect(); } } } function updateFileList() { fileListContainer.innerHTML = ''; selectedFiles.forEach((file, index) => { const fileItem = document.createElement('div'); fileItem.className = 'file-item'; fileItem.innerHTML = ` ${file.name} `; fileListContainer.appendChild(fileItem); }); } // Event listeners for modal interactions modalCloseBtn.addEventListener('click', closeModal); modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); }); fileSelectBtn.addEventListener('click', () => fileInput.click()); fileInput.addEventListener('change', (e) => handleFiles(e.target.files)); fileDropZone.addEventListener('dragover', (e) => { e.preventDefault(); fileDropZone.classList.add('dragover'); }); fileDropZone.addEventListener('dragleave', () => fileDropZone.classList.remove('dragover')); fileDropZone.addEventListener('drop', (e) => { e.preventDefault(); fileDropZone.classList.remove('dragover'); handleFiles(e.dataTransfer.files); }); fileListContainer.addEventListener('click', (e) => { if (e.target.classList.contains('remove-file-btn')) { const index = parseInt(e.target.dataset.index, 10); selectedFiles.splice(index, 1); updateFileList(); if (selectedFiles.length === 0) { processBtn.disabled = true; } } }); processBtn.addEventListener('click', async () => { if (selectedFiles.length === 0 || !currentToolId) return; const tool = toolImplementations[currentToolId]; try { await tool.process(selectedFiles); } catch (error) { console.error('Processing error:', error); showError(`An error occurred: ${error.message}`); } finally { hideLoader(); } }); // --- UTILITY FUNCTIONS --- function showLoader(text = 'Processing...') { loaderText.textContent = text; loader.classList.add('active'); } function hideLoader() { loader.classList.remove('active'); } function showError(message) { hideLoader(); alert(`Error: ${message}`); } function createDownloadLink(data, filename, type) { hideLoader(); const blob = new Blob([data], { type }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.className = 'download-link'; a.href = url; a.download = filename; a.textContent = `Download ${filename}`; outputArea.innerHTML = ''; // Clear previous links outputArea.appendChild(a); } // --- PDF EDITING SPECIFIC FUNCTIONS --- async function renderPdfPageForEditing(pageNum) { if (!pdfDoc || pageNum < 1 || pageNum > totalPages) return; // Save current page's fabric state before switching if (fabricCanvas) { pageRenderData[currentPageNum] = fabricCanvas.toJSON(); } currentPageNum = pageNum; showLoader(`Loading page ${pageNum}...`); const page = await pdfDoc.getPage(pageNum); const viewport = page.getViewport({ scale: 1.5 }); const canvasEl = document.getElementById('editor-canvas'); const context = canvasEl.getContext('2d'); canvasEl.height = viewport.height; canvasEl.width = viewport.width; await page.render({ canvasContext: context, viewport: viewport }).promise; if (fabricCanvas) { fabricCanvas.dispose(); } fabricCanvas = new fabric.Canvas('editor-canvas', { width: viewport.width, height: viewport.height }); // Reload fabric state if it exists for this page if (pageRenderData[pageNum]) { fabricCanvas.loadFromJSON(pageRenderData[pageNum], fabricCanvas.renderAll.bind(fabricCanvas)); } document.getElementById('page-indicator').textContent = `Page ${pageNum} / ${totalPages}`; document.getElementById('prev-page-btn').disabled = pageNum === 1; document.getElementById('next-page-btn').disabled = pageNum === totalPages; hideLoader(); } // Need to add these event listeners dynamically when the edit tool is opened modal.addEventListener('click', (e) => { if (currentToolId !== 'edit-pdf') return; if (e.target.id === 'prev-page-btn') renderPdfPageForEditing(currentPageNum - 1); if (e.target.id === 'next-page-btn') renderPdfPageForEditing(currentPageNum + 1); if (e.target.id === 'add-text-btn') { const text = new fabric.IText('Sample Text', { left: 100, top: 100 }); fabricCanvas.add(text); } if (e.target.id === 'add-rect-btn') { const rect = new fabric.Rect({ left: 150, top: 150, fill: 'rgba(255,0,0,0.5)', width: 100, height: 100 }); fabricCanvas.add(rect); } }); // --- KICK IT OFF --- generateToolCards(); });