File size: 17,088 Bytes
ed58949
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="theme-color" content="#f7f8ff" />
  <meta name="description" content="Try reducing your PDF file size with PDFix. Process your document locally in your browser." />
  <title>Compress PDF — PDFix</title>
  <style>
    :root{--bg:#f7f8ff;--ink:#17182d;--muted:#71738a;--line:#e7e8f2;--primary:#635bff;--primary-dark:#4c43e8;--green:#119b82;--danger:#d94765;--shadow:0 18px 55px rgba(47,43,112,.09)}
    *{box-sizing:border-box}body{margin:0;min-height:100vh;color:var(--ink);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;line-height:1.5;background:radial-gradient(circle at 85% 5%,rgba(177,171,255,.19),transparent 25rem),radial-gradient(circle at 4% 35%,rgba(115,221,211,.12),transparent 22rem),var(--bg)}
    a{color:inherit;text-decoration:none}button,input,select{font:inherit}button{cursor:pointer}.container{width:min(1040px,calc(100% - 32px));margin:0 auto}
    header{padding:20px 0}.nav{display:flex;align-items:center;justify-content:space-between;gap:18px}.brand{display:inline-flex;align-items:center;gap:10px;font-size:1.4rem;font-weight:850;letter-spacing:-1px}.brand-mark{width:39px;height:39px;display:grid;place-items:center;border-radius:13px;color:#fff;background:linear-gradient(145deg,#766dff,#4c43e8);box-shadow:0 8px 20px rgba(99,91,255,.25)}.brand-mark svg{width:23px;height:23px}.brand-name span{color:var(--primary)}.back-link{color:#65677d;font-weight:650;font-size:.92rem}.back-link:hover{color:var(--primary)}
    main{padding:42px 0 70px}.intro{text-align:center;max-width:720px;margin:0 auto 28px}.eyebrow{display:inline-flex;gap:8px;align-items:center;padding:7px 12px;border:1px solid #e4e1ff;border-radius:99px;background:rgba(255,255,255,.8);color:#5a52e9;font-size:.82rem;font-weight:750}
    h1{margin:18px 0 12px;font-size:clamp(2.4rem,5vw,4rem);line-height:1.06;letter-spacing:-2.7px}h1 span{color:var(--primary)}.intro p{margin:0;color:var(--muted);font-size:1.05rem}
    .workspace{max-width:820px;margin:0 auto;border:1px solid rgba(99,91,255,.14);border-radius:24px;padding:12px;background:rgba(255,255,255,.9);box-shadow:var(--shadow)}
    .dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:210px;padding:26px 18px;text-align:center;border:2px dashed #cbc8ff;border-radius:17px;background:linear-gradient(135deg,rgba(238,237,255,.72),rgba(239,253,251,.5));transition:.2s ease}.dropzone.dragover{border-color:var(--primary);background:#efedff;transform:scale(.995)}
    .upload-icon{width:54px;height:54px;display:grid;place-items:center;margin-bottom:13px;border-radius:17px;color:var(--primary);background:#fff;box-shadow:0 7px 18px rgba(71,64,160,.1)}.upload-icon svg{width:28px;height:28px}.dropzone strong{font-size:1.1rem}.dropzone p{margin:5px 0 16px;color:var(--muted);font-size:.9rem}
    .btn{border:0;display:inline-flex;align-items:center;justify-content:center;gap:9px;padding:12px 18px;border-radius:13px;font-weight:750;transition:.2s ease}.btn-primary{color:#fff;background:var(--primary);box-shadow:0 8px 18px rgba(99,91,255,.22)}.btn-primary:hover:not(:disabled){background:var(--primary-dark);transform:translateY(-1px)}.btn-light{color:#4b4d64;background:#fff;border:1px solid var(--line)}.btn-light:hover{border-color:#c9c5ff;color:var(--primary)}button:disabled{opacity:.5;cursor:not-allowed;box-shadow:none}input[type=file]{display:none}
    .file-panel{display:none;padding:22px 10px 8px}.file-panel.visible{display:block}.file-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;padding:13px;border:1px solid var(--line);border-radius:14px;background:#fff}.file-icon{width:42px;height:46px;display:grid;place-items:center;border-radius:11px;color:#e6536a;background:#fff0f2}.file-icon svg{width:23px;height:23px}.file-meta{min-width:0}.file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:700;font-size:.92rem}.file-size{color:var(--muted);font-size:.8rem;margin-top:3px}
    .field{margin-top:18px;padding:17px;border:1px solid var(--line);border-radius:15px;background:#fff}.field-title{font-size:.95rem;font-weight:800;margin-bottom:11px}.quality-options{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.quality-option{display:block;position:relative}.quality-option input{position:absolute;opacity:0;pointer-events:none}.quality-option span{display:block;height:100%;padding:13px;border:1px solid var(--line);border-radius:13px;cursor:pointer;transition:.2s}.quality-option input:checked+span{border-color:var(--primary);background:#f1f0ff;box-shadow:0 0 0 2px rgba(99,91,255,.08)}.quality-option b{display:block;font-size:.9rem}.quality-option small{display:block;margin-top:4px;color:var(--muted);font-size:.78rem;line-height:1.4}.field-note{color:var(--muted);font-size:.82rem;margin:12px 0 0}
    .panel-actions{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-top:17px}.privacy{display:flex;align-items:center;gap:7px;color:var(--muted);font-size:.82rem}.privacy svg{width:17px;height:17px;color:var(--green);flex-shrink:0}
    .status{display:none;margin-top:17px;padding:13px 15px;border-radius:12px;font-size:.9rem}.status.show{display:block}.status.info{background:#efedff;color:#4c43c9}.status.success{background:#e6fbf3;color:#087d68}.status.warning{background:#fff7df;color:#885e00}.status.error{background:#fff0f2;color:#b52b49}
    .result{display:none;margin-top:16px;padding:17px;border:1px solid #c9f0e2;border-radius:15px;background:#f0fcf7}.result.visible{display:block}.result-top{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}.result h2{font-size:1rem;margin:0 0 4px}.result p{margin:0;color:#47796e;font-size:.85rem}.savings{font-size:1.2rem;font-weight:850;color:#087d68}
    .tips{display:grid;grid-template-columns:repeat(3,1fr);gap:15px;max-width:820px;margin:24px auto 0}.tip{padding:18px;border:1px solid var(--line);border-radius:17px;background:rgba(255,255,255,.72)}.tip b{display:block;margin-bottom:5px;font-size:.92rem}.tip p{margin:0;color:var(--muted);font-size:.84rem}
    footer{border-top:1px solid var(--line);padding:24px 0;color:var(--muted);font-size:.85rem}.footer-inner{display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap}.toast{position:fixed;left:50%;bottom:22px;transform:translate(-50%,18px);opacity:0;pointer-events:none;padding:12px 17px;border-radius:13px;background:#20213a;color:#fff;box-shadow:var(--shadow);z-index:10;transition:.22s ease;max-width:calc(100% - 28px);text-align:center;font-size:.9rem}.toast.show{opacity:1;transform:translate(-50%,0)}
    @media(max-width:650px){main{padding-top:30px}h1{letter-spacing:-1.8px}.tips{grid-template-columns:1fr}.quality-options{grid-template-columns:1fr}.panel-actions{align-items:stretch;flex-direction:column}.panel-actions .btn{width:100%}.file-row{grid-template-columns:auto minmax(0,1fr)}}
  </style>
</head>
<body>
<header><div class="container nav">
  <a class="brand" href="index.html" aria-label="PDFix homepage">
    <span class="brand-mark"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M7 3.5h7l4 4V20a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 6 20V5A1.5 1.5 0 0 1 7.5 3.5Z" stroke="currentColor" stroke-width="1.8"/><path d="M14 3.8V8h4M8.5 12h7M8.5 15.5h7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></span>
    <span class="brand-name">PDF<span>ix</span></span>
  </a>
  <a class="back-link" href="index.html">← Back to all tools</a>
</div></header>
<main><div class="container">
  <div class="intro">
    <div class="eyebrow">✦ PDFix PDF tools</div>
    <h1>Compress <span>PDF files</span></h1>
    <p>Try to reduce your PDF file size before sharing or storing it. Choose a compression approach and download the processed file.</p>
  </div>
  <div class="workspace">
    <div class="dropzone" id="dropzone" tabindex="0" role="button" aria-label="Choose or drop a PDF file">
      <div class="upload-icon"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M12 16V4m0 0L7.5 8.5M12 4l4.5 4.5M5 14.5v4A1.5 1.5 0 0 0 6.5 20h11a1.5 1.5 0 0 0 1.5-1.5v-4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
      <strong>Drop one PDF file here</strong><p>Select a PDF to check whether its size can be reduced.</p>
      <button type="button" class="btn btn-primary" id="chooseFile">Select PDF file <span aria-hidden="true">↑</span></button>
      <input id="fileInput" type="file" accept=".pdf,application/pdf" />
    </div>
    <div class="file-panel" id="filePanel">
      <div class="file-row">
        <div class="file-icon"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M6.5 3.5h8l4 4V20a1.5 1.5 0 0 1-1.5 1.5h-10A1.5 1.5 0 0 1 5.5 20V5A1.5 1.5 0 0 1 7 3.5Z" stroke="currentColor" stroke-width="1.7"/><path d="M14.5 3.8V8h4M8 12h8M8 15h6" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg></div>
        <div class="file-meta"><div class="file-name" id="fileName">document.pdf</div><div class="file-size" id="fileSize"></div></div>
        <button class="btn btn-light" id="changeFile" type="button">Change</button>
      </div>
      <div class="field">
        <div class="field-title">Choose a processing level</div>
        <div class="quality-options">
          <label class="quality-option"><input type="radio" name="quality" value="light" checked><span><b>Light</b><small>Try to preserve the original document structure.</small></span></label>
          <label class="quality-option"><input type="radio" name="quality" value="balanced"><span><b>Balanced</b><small>Re-save and clean up PDF objects where possible.</small></span></label>
          <label class="quality-option"><input type="radio" name="quality" value="strong"><span><b>Strong attempt</b><small>Rebuild the PDF structure; results vary by file.</small></span></label>
        </div>
        <p class="field-note">Browser-only limitation: this version re-saves the PDF structure. It does not downsample embedded images or guarantee a smaller file. True image compression requires a more advanced PDF engine.</p>
      </div>
      <div class="panel-actions">
        <div class="privacy"><svg viewBox="0 0 24 24" fill="none"><path d="M12 3 19 6v5c0 4.5-2.9 7.7-7 10-4.1-2.3-7-5.5-7-10V6l7-3Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="m9 12 2 2 4-4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg> Your PDF is processed in your browser.</div>
        <button type="button" class="btn btn-primary" id="compressBtn" disabled>Process PDF <span aria-hidden="true">→</span></button>
      </div>
      <div class="status" id="status" role="status" aria-live="polite"></div>
      <div class="result" id="result">
        <div class="result-top"><div><h2>Your processed PDF is ready</h2><p id="resultDetails"></p></div><div class="savings" id="savings"></div></div>
        <div style="margin-top:14px"><button type="button" class="btn btn-primary" id="downloadBtn">Download PDF ↓</button></div>
      </div>
    </div>
  </div>
  <div class="tips">
    <article class="tip"><b>① Select your PDF</b><p>Choose one PDF file from your device.</p></article>
    <article class="tip"><b>② Process locally</b><p>The browser re-saves the PDF without uploading it to a server.</p></article>
    <article class="tip"><b>③ Check the result</b><p>Compare the original and processed sizes before using the new file.</p></article>
  </div>
</div></main>
<footer><div class="container footer-inner"><a class="brand" href="index.html"><span class="brand-name">PDF<span>ix</span></span></a><span>© <span id="year"></span> PDFix · Your documents, made simple.</span></div></footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
<script>
  const fileInput=document.getElementById('fileInput');
  const dropzone=document.getElementById('dropzone');
  const filePanel=document.getElementById('filePanel');
  const compressBtn=document.getElementById('compressBtn');
  const statusBox=document.getElementById('status');
  const resultBox=document.getElementById('result');
  const toast=document.getElementById('toast');
  let selectedFile=null, outputBlob=null, outputName='pdfix-compressed.pdf', toastTimer;
  document.getElementById('year').textContent=new Date().getFullYear();

  function showToast(message){toast.textContent=message;toast.classList.add('show');clearTimeout(toastTimer);toastTimer=setTimeout(()=>toast.classList.remove('show'),3000)}
  function setStatus(message,type='info'){statusBox.textContent=message;statusBox.className=`status show ${type}`}
  function formatSize(bytes){if(bytes<1024)return `${bytes} B`;if(bytes<1024*1024)return `${(bytes/1024).toFixed(1)} KB`;return `${(bytes/(1024*1024)).toFixed(2)} MB`}
  function choosePdf(files){
    const file=Array.from(files||[]).find(f=>f.name.toLowerCase().endsWith('.pdf')||f.type==='application/pdf');
    if(!file){showToast('Please select a PDF file.');return}
    selectedFile=file;outputBlob=null;resultBox.classList.remove('visible');
    document.getElementById('fileName').textContent=file.name;document.getElementById('fileName').title=file.name;
    document.getElementById('fileSize').textContent=`Original size: ${formatSize(file.size)}`;
    filePanel.style.display='block';filePanel.classList.add('visible');compressBtn.disabled=false;
    statusBox.className='status';fileInput.value='';
  }
  document.getElementById('chooseFile').addEventListener('click',e=>{e.stopPropagation();fileInput.click()});
  document.getElementById('changeFile').addEventListener('click',()=>fileInput.click());
  dropzone.addEventListener('click',e=>{if(!e.target.closest('button'))fileInput.click()});
  dropzone.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();fileInput.click()}});
  fileInput.addEventListener('change',()=>choosePdf(fileInput.files));
  ['dragenter','dragover'].forEach(type=>dropzone.addEventListener(type,e=>{e.preventDefault();dropzone.classList.add('dragover')}));
  ['dragleave','drop'].forEach(type=>dropzone.addEventListener(type,e=>{e.preventDefault();dropzone.classList.remove('dragover')}));
  dropzone.addEventListener('drop',e=>choosePdf(e.dataTransfer.files));

  compressBtn.addEventListener('click',async()=>{
    if(!selectedFile)return;
    if(!window.PDFLib){setStatus('The PDF library could not load. Check your internet connection and reload this page.','error');return}
    const originalLabel=compressBtn.innerHTML;compressBtn.disabled=true;compressBtn.textContent='Processing…';resultBox.classList.remove('visible');
    try{
      setStatus('Reading and re-saving your PDF…','info');
      const {PDFDocument}=window.PDFLib;
      const level=document.querySelector('input[name="quality"]:checked').value;
      const source=await PDFDocument.load(await selectedFile.arrayBuffer());
      const saveOptions={useObjectStreams:level!=='light'};
      const bytes=await source.save(saveOptions);
      outputBlob=new Blob([bytes],{type:'application/pdf'});
      outputName=selectedFile.name.replace(/\.pdf$/i,'')+'-compressed.pdf';
      const before=selectedFile.size,after=outputBlob.size;
      const delta=before-after;
      document.getElementById('resultDetails').textContent=`Original: ${formatSize(before)} · Processed: ${formatSize(after)}`;
      const savings=document.getElementById('savings');
      if(delta>0){savings.textContent=`${((delta/before)*100).toFixed(1)}% smaller`;setStatus('Processing complete. Compare the sizes below; this method may not compress every PDF.','success')}
      else if(delta<0){savings.textContent='Size increased';setStatus('The PDF was re-saved, but its file size increased. This browser-only method cannot compress all PDFs. You can still download it or try another level.','warning')}
      else{savings.textContent='Same size';setStatus('The PDF was re-saved but the file size stayed about the same. Stronger compression may require image optimization or a backend engine.','warning')}
      resultBox.classList.add('visible');
    }catch(error){
      console.error(error);
      setStatus('Could not process this PDF. It may be damaged, password-protected, or use unsupported features.','error');
    }finally{compressBtn.disabled=!selectedFile;compressBtn.innerHTML=originalLabel}
  });
  document.getElementById('downloadBtn').addEventListener('click',()=>{
    if(!outputBlob)return;
    const url=URL.createObjectURL(outputBlob);const link=document.createElement('a');link.href=url;link.download=outputName;document.body.appendChild(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),1500);
  });
</script>
</body>
</html>