Compressing PDF files in the browser requires balancing file size reduction with quality preservation.
Here's how to build a browser-based PDF compression tool with Vue 3 and pdf-lib.
The challenge: Quality vs file size
PDF compression involves:
- Reducing image resolution without visible quality loss
- Removing redundant data and metadata
- Optimizing font embedding
- Maintaining PDF structure integrity
The stack
- Vue 3 with Composition API
- pdf-lib for PDF manipulation
- Canvas API for image re-rendering
- Vite for bundling
The core implementation
<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'
const file = ref<File | null>(null)
const quality = ref(80)
const compressing = ref(false)
const result = ref<Uint8Array | null>(null)
const originalSize = ref(0)
const compressedSize = ref(0)
async function compressPdf() {
if (!file.value) return
compressing.value = true
originalSize.value = file.value.size
const arrayBuffer = await file.value.arrayBuffer()
const pdfDoc = await PDFDocument.load(arrayBuffer, {
updateObjectStreams: true
})
// Recompress embedded images with lower quality
const pages = pdfDoc.getPages()
for (const page of pages) {
const images = await page.embedPngs?.() || []
// Note: pdf-lib doesn't directly support image re-encoding
// For full compression, we'd use pdf.js to re-render pages
}
result.value = await pdfDoc.save({
updateObjectStreams: true,
objectsPerTick: 50
})
compressedSize.value = result.value.length
compressing.value = false
}
</script>
Key implementation details
1. Object stream optimization
Enable object streams to compress internal PDF structures:
await pdfDoc.save({ updateObjectStreams: true })
2. Image quality control
For image-heavy PDFs, re-render pages through canvas at reduced quality:
const canvas = document.createElement('canvas')
canvas.width = pageWidth * (quality / 100)
canvas.height = pageHeight * (quality / 100)
const ctx = canvas.getContext('2d')!
await page.render({ canvasContext: ctx, viewport }).promise
3. Progress tracking
Use objectsPerTick to prevent UI freezing during large file compression:
await pdfDoc.save({
updateObjectStreams: true,
objectsPerTick: 50
})
Limitations
No true image recompression
pdf-lib doesn't directly re-encode embedded images.
Solution: Use pdf.js to re-render pages and create new PDF from scratch.
Quality loss with heavy compression
Aggressive compression can make text blurry.
Solution: Add a preview feature and let users test different quality levels.
Memory constraints
Large PDFs can exhaust browser memory during compression.
Solution: Process pages in batches and add progress indicators.
Summary
Building a browser-based PDF compression tool involves:
- Using pdf-lib for structure optimization
- Re-rendering pages for image quality control
- Using object streams for internal compression
- Adding preview and progress features
Try it at en.sotool.top/compress.
Top comments (0)