DEV Community

sunshey
sunshey

Posted on

How to Compress PDF Files in the Browser with Vue 3 and pdf-lib

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:

  1. Reducing image resolution without visible quality loss
  2. Removing redundant data and metadata
  3. Optimizing font embedding
  4. 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>
Enter fullscreen mode Exit fullscreen mode

Key implementation details

1. Object stream optimization

Enable object streams to compress internal PDF structures:

await pdfDoc.save({ updateObjectStreams: true })
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

3. Progress tracking

Use objectsPerTick to prevent UI freezing during large file compression:

await pdfDoc.save({ 
  updateObjectStreams: true,
  objectsPerTick: 50 
})
Enter fullscreen mode Exit fullscreen mode

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:

  1. Using pdf-lib for structure optimization
  2. Re-rendering pages for image quality control
  3. Using object streams for internal compression
  4. Adding preview and progress features

Try it at en.sotool.top/compress.

Top comments (0)