DEV Community

sunshey
sunshey

Posted on

How to Convert PDF to Word in the Browser with Vue 3 and pdf-lib

Converting PDF to Word seems straightforward, but the reality is more complex. PDF stores text as character coordinates, while Word uses structured paragraphs. Bridging this gap requires careful text extraction and order reconstruction.

Here's how to build a browser-based PDF to Word converter with Vue 3 and pdf-lib.

The challenge: PDF vs Word

PDF is a presentation format — text is positioned precisely on the page. Word is an editing format — text flows in paragraphs with styles. Converting between them means:

  1. Extracting text from PDF coordinates
  2. Reconstructing reading order
  3. Generating structured DOCX output

The stack

  • Vue 3 with Composition API
  • pdf-lib for PDF parsing
  • docx for Word document generation
  • Vite for bundling

The core implementation

<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'
import { Document, Paragraph, TextRun } from 'docx'

const file = ref<File | null>(null)
const processing = ref(false)
const result = ref<Blob | null>(null)

async function convertPdfToWord() {
  if (!file.value) return
  processing.value = true

  const arrayBuffer = await file.value.arrayBuffer()
  const pdf = await PDFDocument.load(arrayBuffer)
  const pages = pdf.getPages()

  const allChunks: TextChunk[] = []

  for (const page of pages) {
    const textContent = await page.getTextContent()

    for (const item of textContent.items) {
      allChunks.push({
        text: item.text,
        x: item.transform[4],
        y: item.transform[5],
        size: item.size
      })
    }
  }

  // Sort by reading order
  const sorted = sortByReadingOrder(allChunks)

  // Generate DOCX
  const doc = new Document({
    sections: [{
      properties: {},
      children: sorted.map(chunk => 
        new Paragraph({
          children: [new TextRun(chunk.text)]
        })
      )
    }]
  })

  const blob = await doc.pack()
  result.value = blob
  processing.value = false
}

interface TextChunk {
  text: string
  x: number
  y: number
  size: number
}

function sortByReadingOrder(chunks: TextChunk[]): TextChunk[] {
  return chunks.sort((a, b) => {
    if (Math.abs(a.y - b.y) > 5) {
      return b.y - a.y  // Top to bottom
    }
    return a.x - b.x    // Left to right
  })
}
</script>
Enter fullscreen mode Exit fullscreen mode

Key implementation details

1. Text extraction from PDF

pdf-lib's getTextContent() returns an array of text items with position and size:

const textContent = await page.getTextContent()
for (const item of textContent.items) {
  console.log(item.text, item.transform[4], item.transform[5])
}
Enter fullscreen mode Exit fullscreen mode

2. Reading order reconstruction

PDF stores text in drawing order, not reading order. We sort by:

  • Y coordinate (descending): Top to bottom
  • X coordinate (ascending): Left to right
function sortByReadingOrder(chunks: TextChunk[]): TextChunk[] {
  return chunks.sort((a, b) => {
    if (Math.abs(a.y - b.y) > 5) {
      return b.y - a.y
    }
    return a.x - b.x
  })
}
Enter fullscreen mode Exit fullscreen mode

3. DOCX generation

The docx library creates Word documents programmatically:

const doc = new Document({
  sections: [{
    children: chunks.map(chunk => 
      new Paragraph({ children: [new TextRun(chunk.text)] })
    )
  }]
})
Enter fullscreen mode Exit fullscreen mode

4. Handling edge cases

  • Empty text: Skip items with empty text
  • Overlapping text: Detect and handle overlapping character positions
  • Multi-column: Group by X coordinate ranges for column detection
function detectColumns(chunks: TextChunk[], threshold = 100): TextChunk[][] {
  const xs = chunks.map(c => c.x)
  const min = Math.min(...xs)
  const max = Math.max(...xs)
  const range = max - min

  return Array.from({ length: Math.ceil(range / threshold) }, (_, i) =>
    chunks.filter(c => c.x >= min + i * threshold && c.x < min + (i + 1) * threshold)
  ).filter(col => col.length > 0)
}
Enter fullscreen mode Exit fullscreen mode

Limitations

Scanned PDFs

Scanned PDFs are images, not text. pdf-lib can't extract text from images.

Solution: Detect empty text content and show error message.

Complex layouts

Multi-column, tables, and floating text are hard to reconstruct accurately.

Solution: Simple layout detection and graceful degradation.

Font encoding

Custom fonts may not map correctly to Word fonts.

Solution: Use standard font fallbacks.

Summary

Building a browser-based PDF to Word converter involves:

  1. Loading PDF with pdf-lib
  2. Extracting text and coordinates
  3. Sorting by reading order
  4. Generating DOCX with docx library
  5. Handling edge cases (scanned PDFs, complex layouts)

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

Top comments (0)