Theologians Press
Writing Tools / File Converter

File Converter

Convert documents and images between formats. Everything happens in your own browser, and your file is never uploaded to a server.

📄
Drag a file here, or click to choose one
Images: PNG, JPG, WEBP, BMP, GIF, SVG  ·  Documents: PDF, DOCX, TXT, Markdown

No file size limits imposed by us, no account needed, no watermark. Very large PDFs or images may be slow depending on your device, since conversion runs locally on your machine.

What it can do

Images: convert PNG, JPG, WEBP, BMP, GIF, or SVG into PNG, JPG, or WEBP.

PDF: extract the text as a .txt file, or export every page as PNG images.

Word documents (.docx): extract as plain text, as HTML, or export as a PDF.

Text & Markdown: export as a PDF or as a Word (.docx) document.

Common Questions

How do I convert a PDF to text for free?

Drop your PDF into the tool above, choose "Text (.txt)" as the output, and press Convert. The text is extracted right in your browser and downloads as a plain text file.

How do I turn a PDF into images?

Choose "Images (.png, page by page)" as the output format. Each page of the PDF is rendered as its own PNG image; if there is more than one page, they download together in a .zip file.

How do I convert a Word document to HTML or plain text?

Open your .docx file and choose either "Plain text (.txt)" or "HTML (.html)" as the output. Both conversions preserve the document's paragraph structure.

How do I turn a text or Markdown file into a Word document or PDF?

Open a .txt or .md file and choose "PDF (.pdf)" or "Word document (.docx)" as the output. This is useful for turning a plain draft into something ready to submit or share.

Is my file uploaded anywhere?

No. Every conversion runs locally in your browser using pdf.js, mammoth.js, and jsPDF. Your file is never sent to our server or anyone else's.

Do I need an account, or is there a watermark?

No account is needed, and converted files have no watermark. We do not impose a file size limit either; the only ceiling is what your own device can comfortably handle.

Does it work on a phone or tablet?

Yes. It runs in any modern browser, including on mobile, since the conversion happens on your own device rather than a server.

'; return new Blob([html], { type: 'text/html' }); }); }); } function docxToText(file) { return file.arrayBuffer().then(function (buf) { return mammoth.extractRawText({ arrayBuffer: buf }).then(function (result) { return new Blob([result.value], { type: 'text/plain' }); }); }); } // ---------- Text / Markdown -> PDF ---------- function textStringToPdf(text) { var jsPDFCtor = window.jspdf.jsPDF; var doc = new jsPDFCtor({ unit: 'pt', format: 'letter' }); var margin = 54; var pageWidth = doc.internal.pageSize.getWidth(); var pageHeight = doc.internal.pageSize.getHeight(); var usableWidth = pageWidth - margin * 2; var lineHeight = 14; doc.setFont('times', 'normal'); doc.setFontSize(11); var lines = doc.splitTextToSize(text, usableWidth); var y = margin; lines.forEach(function (line) { if (y > pageHeight - margin) { doc.addPage(); y = margin; } doc.text(line, margin, y); y += lineHeight; }); return doc.output('blob'); } function textToPdf(file) { return file.text().then(textStringToPdf); } function docxToPdf(file) { return file.arrayBuffer().then(function (buf) { return mammoth.extractRawText({ arrayBuffer: buf }).then(function (result) { return textStringToPdf(result.value); }); }); } // ---------- Text / Markdown -> DOCX (hand-built minimal OOXML) ---------- function xmlEscape(s) { return s.replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); } function textToDocx(file) { return file.text().then(function (text) { var lines = text.split(/\r\n|\r|\n/); var body = lines.map(function (line) { if (line.trim() === '') return ''; return '' + xmlEscape(line) + ''; }).join(''); var documentXml = '' + '' + '' + body + ''; var contentTypesXml = '' + '' + '' + '' + '' + ''; var relsXml = '' + '' + '' + ''; var appXml = '' + '' + 'Theologians Press File Converter'; var zip = new JSZip(); zip.file('[Content_Types].xml', contentTypesXml); zip.folder('_rels').file('.rels', relsXml); zip.folder('word').file('document.xml', documentXml); zip.folder('docProps').file('app.xml', appXml); return zip.generateAsync({ type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }); }); } // ---------- Dispatch ---------- function runConversion() { if (!currentFile) return; var targetExt = outputFormat.value; var selectedOption = outputFormat.options[outputFormat.selectedIndex]; var targetMime = selectedOption ? selectedOption.getAttribute('data-mime') : null; var outName = baseName(currentFile.name); convertBtn.disabled = true; setBusy('Converting…'); var task; if (IMAGE_EXTS.indexOf(currentExt) !== -1) { task = convertImage(currentFile, targetMime).then(function (blob) { downloadBlob(blob, outName + '.' + targetExt); setStatus('Done. Your file has downloaded as ' + outName + '.' + targetExt + '.', 'success'); }); } else if (currentExt === 'pdf' && targetExt === 'txt') { task = pdfToText(currentFile).then(function (blob) { downloadBlob(blob, outName + '.txt'); setStatus('Done. Your file has downloaded as ' + outName + '.txt.', 'success'); }); } else if (currentExt === 'pdf' && targetExt === 'png') { task = pdfToImages(currentFile).then(function (result) { if (result.single) { downloadBlob(result.single, outName + '.png'); setStatus('Done. Your file has downloaded as ' + outName + '.png.', 'success'); } else { downloadBlob(result.zip, outName + '-pages.zip'); setStatus('Done. Every page has downloaded as PNGs inside ' + outName + '-pages.zip.', 'success'); } }); } else if (currentExt === 'docx' && targetExt === 'html') { task = docxToHtml(currentFile).then(function (blob) { downloadBlob(blob, outName + '.html'); setStatus('Done. Your file has downloaded as ' + outName + '.html.', 'success'); }); } else if (currentExt === 'docx' && targetExt === 'txt') { task = docxToText(currentFile).then(function (blob) { downloadBlob(blob, outName + '.txt'); setStatus('Done. Your file has downloaded as ' + outName + '.txt.', 'success'); }); } else if (currentExt === 'docx' && targetExt === 'pdf') { task = docxToPdf(currentFile).then(function (blob) { downloadBlob(blob, outName + '.pdf'); setStatus('Done. Your file has downloaded as ' + outName + '.pdf.', 'success'); }); } else if ((currentExt === 'txt' || currentExt === 'md') && targetExt === 'pdf') { task = textToPdf(currentFile).then(function (blob) { downloadBlob(blob, outName + '.pdf'); setStatus('Done. Your file has downloaded as ' + outName + '.pdf.', 'success'); }); } else if ((currentExt === 'txt' || currentExt === 'md') && targetExt === 'docx') { task = textToDocx(currentFile).then(function (blob) { downloadBlob(blob, outName + '.docx'); setStatus('Done. Your file has downloaded as ' + outName + '.docx.', 'success'); }); } else { task = Promise.reject(new Error('That conversion isn’t supported yet.')); } task.then(function () { enterResultFocus(); }).catch(function (err) { console.error(err); setStatus('Conversion failed: ' + (err && err.message ? err.message : 'unknown error') + '.', 'error'); }).then(function () { convertBtn.disabled = false; }); } convertBtn.addEventListener('click', runConversion); })();