DEV Community

sunshey
sunshey

Posted on

How to Convert Images to PDF in the Browser with Vue 3

Combining images into PDFs is a common need, but handling multiple formats and maintaining quality requires careful implementation.

Here's how to build a browser-based image to PDF converter with Vue 3.

The challenge: Multiple formats, one output

Images come in various formats (JPG, PNG, WebP, HEIC), but PDF only supports specific image types. Converting between them requires:

  1. Format detection and normalization
  2. Quality preservation
  3. Layout management
  4. Memory optimization

The stack

  • Vue 3 with Composition API
  • pdf-lib for PDF generation
  • Canvas API for image processing
  • Vite for bundling

The core implementation

<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'

const files = ref<File[]>([])
const converting = ref(false)
const result = ref<Blob | null>(null)

async function convertToPdf() {
  if (files.value.length === 0) return
  converting.value = true

  const pdfDoc = await PDFDocument.create()

  for (const file of files.value) {
    const arrayBuffer = await file.arrayBuffer()

    let imageBytes: Uint8Array
    let imageType: 'jpg' | 'png'

    if (file.type === 'image/jpeg') {
      imageBytes = arrayBuffer
      imageType = 'jpg'
    } else if (file.type === 'image/png') {
      imageBytes = arrayBuffer
      imageType = 'png'
    } else {
      // Convert to JPEG using Canvas
      imageBytes = await convertToJpeg(arrayBuffer, file.type)
      imageType = 'jpg'
    }

    // Add page (A4 size)
    const page = pdfDoc.addPage([612, 792])

    // Embed and draw image
    const img = imageType === 'jpg' 
      ? await pdfDoc.embedJpg(imageBytes)
      : await pdfDoc.embedPng(imageBytes)

    page.drawImage(img, {
      x: 0, y: 0,
      width: page.getWidth(),
      height: page.getHeight()
    })
  }

  result.value = await pdfDoc.save()
  converting.value = false
}

// Convert any image format to JPEG
async function convertToJpeg(arrayBuffer: ArrayBuffer, mimeType: string): Promise<Uint8Array> {
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')!

  return new Promise((resolve) => {
    const img = new Image()
    img.onload = () => {
      canvas.width = img.width
      canvas.height = img.height
      ctx.drawImage(img, 0, 0)
      canvas.toBlob((blob) => {
        if (blob) {
          const reader = new FileReader()
          reader.onload = () => resolve(new Uint8Array(reader.result as ArrayBuffer))
          reader.readAsArrayBuffer(blob)
        }
      }, 'image/jpeg', 0.95)
    }
    img.src = URL.createObjectURL(new Blob([arrayBuffer]))
  })
}
</script>
Enter fullscreen mode Exit fullscreen mode

Key implementation details

1. Format detection

Check file type and handle accordingly:

if (file.type === 'image/jpeg') {
  // Direct embedding
} else if (file.type === 'image/png') {
  // Direct embedding
} else {
  // Convert via Canvas
}
Enter fullscreen mode Exit fullscreen mode

2. Canvas conversion

For unsupported formats, use Canvas to convert:

const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
canvas.toBlob((blob) => { /* convert to bytes */ }, 'image/jpeg', 0.95)
Enter fullscreen mode Exit fullscreen mode

3. Image sizing

Handle different image ratios:

function fitImage(imgWidth: number, imgHeight: number, pageWidth: number, pageHeight: number) {
  const scale = Math.min(pageWidth / imgWidth, pageHeight / imgHeight)
  return {
    width: imgWidth * scale,
    height: imgHeight * scale
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Memory management

Process images in batches to avoid memory issues:

const BATCH_SIZE = 20
for (let i = 0; i < files.value.length; i += BATCH_SIZE) {
  const batch = files.value.slice(i, i + BATCH_SIZE)
  await processBatch(batch, pdfDoc)
}
Enter fullscreen mode Exit fullscreen mode

Limitations

HEIC support

HEIC images require additional decoding libraries.

Solution: Prompt users to convert HEIC to JPG first.

Large files

Very large images may cause memory issues.

Solution: Implement image resizing or chunked processing.

WebP compatibility

WebP support varies across PDF readers.

Solution: Convert WebP to JPG for maximum compatibility.

Summary

Building a browser-based image to PDF converter involves:

  1. Detecting image formats
  2. Normalizing to supported types
  3. Embedding in PDF pages
  4. Managing memory for large batches

Try it at en.sotool.top/image-to-pdf.

Top comments (0)