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:
- Extracting text from PDF coordinates
- Reconstructing reading order
- 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>
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])
}
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
})
}
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)] })
)
}]
})
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)
}
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:
- Loading PDF with pdf-lib
- Extracting text and coordinates
- Sorting by reading order
- Generating DOCX with docx library
- Handling edge cases (scanned PDFs, complex layouts)
Try it at en.sotool.top/pdf-to-word.
Top comments (0)