Welcome to PDFTim 👋

All PDF tools free in your browser. No uploads to servers.

19+
Free Tools
100%
Browser-based
₹199
Pro/month
Advertisement
All Tools
🔀
Merge PDF
Combine PDFs into one
✂️
Split PDF
Split by page range
🗜️
Compress PDF
Reduce file size
🔄
Rotate PDF
Fix orientation
🖼️
JPG to PDF
Images → PDF
🖼️
PNG to PDF
PNG images → PDF
📝
Word to PDF
DOCX → PDF
📷
PDF to JPG
Pages → JPG ZIP
📷
PDF to PNG
Pages → PNG ZIP
📤
Extract Pages
Pull specific pages
🗑️
Delete Pages
Remove pages
🔢
Page Numbers
Add numbering
💧
Watermark
Text watermark
🔐
Protect PDF
Password lock
🔓
Unlock PDF
Remove password
🔍
OCR
Image → text
AI
📦
Compress Image
Reduce image size
🔲
Resize Image
Change dimensions
🔁
Convert Image
JPG↔PNG↔WebP
Ad
'); return; } const worker = await Tesseract.createWorker(lang, 1, { logger: m => { if(m.status==='recognizing text') setProgress(id, 20+m.progress*70, `Recognizing… ${Math.round(m.progress*100)}%`); } }); const { data: { text } } = await worker.recognize(src); await worker.terminate(); hideProgress(id); if(outEl){outEl.textContent=text||(No text detected);outEl.classList.add('show');} const blob = new Blob([text],{type:'text/plain'}); showResult(id,'Text extracted!',`${text.split('\n').length} lines · Click to download TXT`,blob,'ocr_result.txt'); }); } function compressImg(dataUrl, fmt, quality) { return new Promise((res,rej) => { const img = new Image(); img.onload = () => { const c = document.createElement('canvas'); c.width=img.naturalWidth; c.height=img.naturalHeight; c.getContext('2d').drawImage(img,0,0); c.toBlob(b => b?res(b):rej(new Error('Failed')), `image/${fmt}`, quality); }; img.onerror=rej; img.src=dataUrl; }); } async function runImgCompress() { const id = 'imgcompress'; await gatedRun(id, async () => { if (!toolFiles[id]?.length) return; clearResult(id); const fmtMap = ['jpeg','png','webp']; const fmt = fmtMap[document.getElementById('ic-fmt').selectedIndex||0]; const quality = parseInt(document.getElementById('ic-q').value||'75')/100; const files = toolFiles[id]; if (files.length === 1) { setProgress(id,40,'Compressing…'); const blob = await compressImg(await readDataURL(files[0]), fmt, quality); const ext = fmt==='jpeg'?'jpg':fmt; hideProgress(id); const orig = files[0].size; showResult(id,`Compressed! ${Math.max(0,((orig-blob.size)/orig*100)).toFixed(1)}% smaller`,`${fmtBytes(orig)} → ${fmtBytes(blob.size)}`,blob,`compressed.${ext}`); } else { const zip = new JSZip(); for(let i=0;i { if (!toolFiles[id]?.[0]) return; clearResult(id); setProgress(id,30,'Loading…'); const f = toolFiles[id][0]; const dataUrl = await readDataURL(f); const img = await new Promise((res,rej)=>{const i=new Image();i.onload=()=>res(i);i.onerror=rej;i.src=dataUrl;}); const modeIdx = document.getElementById('ir-mode').selectedIndex; let tw, th; if (modeIdx === 1) { const pct = parseFloat(document.getElementById('ir-pct').value||'50')/100; tw=Math.round(img.naturalWidth*pct); th=Math.round(img.naturalHeight*pct); } else { tw = parseInt(document.getElementById('ir-w').value)||img.naturalWidth; th = parseInt(document.getElementById('ir-h').value)||img.naturalHeight; if (document.getElementById('ir-ratio').checked) { const ratio = img.naturalWidth/img.naturalHeight; if (!document.getElementById('ir-h').value) th=Math.round(tw/ratio); else if (!document.getElementById('ir-w').value) tw=Math.round(th*ratio); } } setProgress(id,65,'Resizing…'); const c=document.createElement('canvas'); c.width=tw; c.height=th; c.getContext('2d').drawImage(img,0,0,tw,th); const ext=f.name.split('.').pop().toLowerCase(); const mime=ext==='png'?'image/png':'image/jpeg'; const blob=await new Promise(res=>c.toBlob(res,mime,0.92)); hideProgress(id); showResult(id,`Resized to ${tw}×${th}px!`,fmtBytes(blob.size),blob,`resized.${ext}`); }); } async function runImgConvert() { const id = 'imgconvert'; await gatedRun(id, async () => { if (!toolFiles[id]?.length) return; clearResult(id); const fmtMap = ['jpeg','png','webp']; const fmt = fmtMap[document.getElementById('conv-to').selectedIndex||0]; const quality = parseInt(document.getElementById('conv-q').value||'90')/100; const files = toolFiles[id]; const ext = fmt==='jpeg'?'jpg':fmt; if (files.length === 1) { setProgress(id,40,'Converting…'); const blob = await compressImg(await readDataURL(files[0]), fmt, quality); hideProgress(id); showResult(id,`Converted to ${ext.toUpperCase()}!`,fmtBytes(blob.size),blob,`converted.${ext}`); } else { const zip = new JSZip(); for(let i=0;i { try { (adsbygoogle = window.adsbygoogle || []).push({}); } catch(e) {} // Init Supabase session check if (supabase) { supabase.auth.getSession().then(({data:{session}}) => { if (session?.user) { currentUser = session.user; loadUserPlan().then(updateNavUI); } }); } });