pdftext: Extract Resume Text in the Browser (No Upload to sketchy sites)
HR workflow needed plain text from PDFs on a locked-down laptop. pdf.js in a static page — page-by-page extraction with progress bar.
Tested on: pdf.js 4.8.69, Chrome 136 Constraint: No server. Files never leave the machine.
Full extractor
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PDF Text Extractor</title>
<style>
body { font-family: system-ui; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
textarea { width: 100%; min-height: 240px; font-family: monospace; font-size: 13px; }
progress { width: 100%; height: 8px; }
.err { color: #b91c1c; }
</style>
</head>
<body>
<h1>PDF Text Extractor</h1>
<input type="file" id="file" accept="application/pdf">
<progress id="prog" value="0" max="100" hidden></progress>
<p id="status"></p>
<textarea id="out" readonly placeholder="Extracted text"></textarea>
<button id="copy" disabled>Copy</button>
<script type="module">
import * as pdfjsLib from 'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.8.69/build/pdf.min.mjs';
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.8.69/build/pdf.worker.min.mjs';
const fileInput = document.getElementById('file');
const out = document.getElementById('out');
const status = document.getElementById('status');
const prog = document.getElementById('prog');
const copyBtn = document.getElementById('copy');
fileInput.onchange = async () => {
const file = fileInput.files[0];
if (!file) return;
out.value = '';
status.textContent = 'Loading...';
status.className = '';
prog.hidden = false;
prog.value = 0;
try {
const buf = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: buf }).promise;
const pages = pdf.numPages;
let full = '';
for (let i = 1; i <= pages; i++) {
const page = await pdf.getPage(i);
const content = await page.getTextContent();
const text = content.items.map(it => it.str).join(' ');
full += '\n--- Page ' + i + ' ---\n' + text + '\n';
prog.value = Math.round((i / pages) * 100);
status.textContent = 'Page ' + i + ' of ' + pages;
}
out.value = full.trim();
copyBtn.disabled = false;
status.textContent = 'Done — ' + pages + ' pages, ' + full.length + ' characters';
} catch (e) {
status.textContent = e.message;
status.className = 'err';
}
};
copyBtn.onclick = () => navigator.clipboard.writeText(out.value);
</script>
</body>
</html>
Scanned PDF = empty output
Done — 2 pages, 0 characters
pdf.js extracts text layers only. Scanned images need OCR (Tesseract.js) — document that limitation upfront.
Worker CORS on file://
Opening HTML directly fails worker load. Serve via npx serve or deploy to static host.
Password-protected PDFs
PasswordException: No password given
Add optional password field wired to getDocument({ password }) if needed.
Verify
Digital PDF resume → text appears with page markers 20-page doc → progress bar advances Copy button fills clipboard