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 careful balancing of file size reduction and quality preservation. Here's how to build a browser-based PDF compression tool with Vue 3 and pdf-lib.

The challenge: Quality vs. Size

PDF compression involves trade-offs:

  1. Reducing image quality to shrink file size
  2. Removing unnecessary metadata
  3. Subsetting or removing fonts
  4. Optimizing vector graphics

The stack

  • Vue 3 with Composition API
  • pdf-lib for PDF manipulation
  • 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<'low' | 'medium' | 'high'>('medium')
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

  const arrayBuffer = await file.value.arrayBuffer()
  originalSize.value = arrayBuffer.byteLength

  const pdf = await PDFDocument.load(arrayBuffer, {
    ignoreEncryption: true,
  })

  // Remove unnecessary metadata
  pdf.setTitle('')
  pdf.setAuthor('')
  pdf.setSubject('')
  pdf.setKeywords([])
  pdf.setProducer('')
  pdf.setCreator('')

  // Compress embedded images by re-encoding
  const pages = pdf.getPages()
  for (const page of pages) {
    const width = page.getWidth()
    const height = page.getHeight()
    // Trigger image compression during save
    page.setSize(width, height)
  }

  const options = {
    useObjectStreams: true,
    addDefaultPage: false,
  }

  result.value = await pdf.save(options)
  compressedSize.value = result.value.byteLength
  compressing.value = false
}

const compressionRatio = computed(() => {
  if (!originalSize.value || !compressedSize.value) return 0
  return Math.round((1 - compressedSize.value / originalSize.value) * 100)
})
</script>
Enter fullscreen mode Exit fullscreen mode

Key implementation details

1. Metadata cleanup

Removing metadata reduces file size without affecting content:

pdf.setTitle('')
pdf.setAuthor('')
pdf.setSubject('')
pdf.setKeywords([])
pdf.setProducer('')
pdf.setCreator('')
Enter fullscreen mode Exit fullscreen mode

2. Object streams

Enable object streams for better compression:

const options = { useObjectStreams: true }
const compressed = await pdf.save(options)
Enter fullscreen mode Exit fullscreen mode

3. Quality settings

Adjust compression aggressiveness based on user preference:

async function compressWithQuality(arrayBuffer: ArrayBuffer, level: 'low' | 'medium' | 'high'): Promise<Uint8Array> {
  const pdf = await PDFDocument.load(arrayBuffer, { ignoreEncryption: true })

  // Remove metadata
  pdf.setTitle('')
  pdf.setAuthor('')
  pdf.setSubject('')
  pdf.setKeywords([])
  pdf.setProducer('')
  pdf.setCreator('')

  const options = { useObjectStreams: level !== 'low' }
  return await pdf.save(options)
}
Enter fullscreen mode Exit fullscreen mode

4. Progress feedback

Show compression ratio in real-time:

const compressionRatio = computed(() => {
  if (!originalSize.value || !compressedSize.value) return 0
  return Math.round((1 - compressedSize.value / originalSize.value) * 100)
})
Enter fullscreen mode Exit fullscreen mode

Limitations

No image re-encoding

pdf-lib doesn't re-encode embedded images for maximum compression.

Solution: For image-heavy PDFs, consider using a dedicated compression tool or desktop software.

Font subsetting

pdf-lib doesn't subset fonts automatically.

Solution: This is handled at the browser level by the PDF viewer's rendering engine.

Large files

Very large PDFs may cause memory issues.

Solution: Process in smaller batches or use Web Workers.

Summary

Building a browser-based PDF compression tool involves:

  1. Loading the PDF with pdf-lib
  2. Removing unnecessary metadata
  3. Enabling object streams for better compression
  4. Showing compression ratio to the user

Try it at en.sotool.top/compress.

Top comments (0)