Client-side
PDF TO TEXT
Extract raw text from PDF files directly in your web browser. Copy, edit, or download the text immediately.
Drag & drop a PDF file here, or click to browse
Supports single .pdf file
About PDF to Text Extractor
Extract raw, editable text content from your PDF documents instantly. The tool uses pdf.js page parsing to extract text layout representations and display them inside a text editor window where you can copy, edit, or download the results.
Perfect for extracting articles, quotes, or notes from books, slide decks, or scanned documents. Runs 100% locally with zero server logs, guaranteeing absolute privacy.
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
// Set up PDF.js worker
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
let currentFile = null;
let currentPdfDoc = null;
let fileNameStr = '';
const uploadZone = document.getElementById('uploadZone');
const fileInput = document.getElementById('fileInput');
const configPanel = document.getElementById('configPanel');
const fileNameEl = document.getElementById('fileName');
const pageCountEl = document.getElementById('pageCount');
const extractBtn = document.getElementById('extractBtn');
const loadingContainer = document.getElementById('loadingContainer');
const progressText = document.getElementById('progressText');
const progressBar = document.getElementById('progressBar');
const previewBox = document.getElementById('previewBox');
const previewTextarea = document.getElementById('previewTextarea');
const copyBtn = document.getElementById('copyBtn');
const downloadBtn = document.getElementById('downloadBtn');
// Drag and drop event listeners
['dragenter', 'dragover'].forEach(eventName => {
uploadZone.addEventListener(eventName, e => {
e.preventDefault();
uploadZone.classList.add('dragover');
}, false);
});
['dragleave', 'drop'].forEach(eventName => {
uploadZone.addEventListener(eventName, e => {
e.preventDefault();
uploadZone.classList.remove('dragover');
}, false);
});
uploadZone.addEventListener('drop', e => {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) handleFile(files[0]);
});
fileInput.addEventListener('change', e => {
if (e.target.files.length > 0) handleFile(e.target.files[0]);
});
async function handleFile(file) {
if (file.type !== 'application/pdf') return;
currentFile = file;
fileNameStr = file.name;
try {
const fileReader = new FileReader();
fileReader.onload = async function() {
const typedarray = new Uint8Array(this.result);
currentPdfDoc = await pdfjsLib.getDocument(typedarray).promise;
fileNameEl.textContent = file.name;
pageCountEl.textContent = `${currentPdfDoc.numPages} page${currentPdfDoc.numPages > 1 ? 's' : ''}`;
configPanel.classList.remove('hidden');
previewBox.classList.add('hidden');
};
fileReader.readAsArrayBuffer(file);
} catch (err) {
console.error(err);
alert('Could not read PDF file.');
}
}
extractBtn.addEventListener('click', async () => {
if (!currentPdfDoc) return;
configPanel.classList.add('hidden');
loadingContainer.classList.remove('hidden');
previewBox.classList.add('hidden');
const totalPages = currentPdfDoc.numPages;
let fullText = '';
try {
for (let pageNum = 1; pageNum <= totalPages; pageNum++) {
progressText.textContent = `Extracting text from page ${pageNum} of ${totalPages}...`;
const percentage = Math.round((pageNum - 1) / totalPages * 100);
progressBar.style.width = `${percentage}%`;
const page = await currentPdfDoc.getPage(pageNum);
const textContent = await page.getTextContent();
let pageText = '';
let lastY = -1;
for (const item of textContent.items) {
// Check if we are on a new line roughly based on the y-coordinate of the item
if (lastY !== -1 && Math.abs(item.transform[5] - lastY) > 8) {
pageText += '\n';
}
pageText += item.str;
lastY = item.transform[5];
}
fullText += `--- PAGE ${pageNum} ---\n\n${pageText.trim()}\n\n`;
}
progressBar.style.width = '100%';
previewTextarea.value = fullText.trim();
// Prepare text download
const blob = new Blob([fullText], { type: 'text/plain;charset=utf-8' });
const blobUrl = URL.createObjectURL(blob);
downloadBtn.href = blobUrl;
const nameWithoutExt = fileNameStr.replace(/\.[^/.]+$/, "");
downloadBtn.download = `${nameWithoutExt}_extracted.txt`;
loadingContainer.classList.add('hidden');
previewBox.classList.remove('hidden');
} catch (err) {
console.error(err);
alert('An error occurred during text extraction: ' + err.message);
loadingContainer.classList.add('hidden');
configPanel.classList.remove('hidden');
}
});
copyBtn.addEventListener('click', () => {
previewTextarea.select();
document.execCommand('copy');
const originalText = copyBtn.textContent;
copyBtn.textContent = 'Copied!';
setTimeout(() => {
copyBtn.textContent = originalText;
}, 2000);
});