Converting Word to PDF seems simple, but the reality is more complex. Word uses flow layout while PDF uses fixed layout. Bridging this gap requires careful rendering and pagination.
Here's how to build a browser-based Word to PDF converter with Vue 3.
The challenge: Flow vs Fixed layout
Word documents flow content across pages dynamically. PDFs have fixed page sizes and positions. Converting between them means:
- Parsing DOCX structure
- Rendering to HTML/CSS
- Capturing as images
- Assembling into PDF pages
The stack
- Vue 3 with Composition API
- docx-parser for DOCX reading
- html2canvas for rendering
- pdf-lib for PDF generation
- Vite for bundling
The core implementation
<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'
import html2canvas from 'html2canvas'
const file = ref<File | null>(null)
const converting = ref(false)
const result = ref<Blob | null>(null)
async function convertWordToPdf() {
if (!file.value) return
converting.value = true
// 1. Parse DOCX
const arrayBuffer = await file.value.arrayBuffer()
const doc = await parseDocx(arrayBuffer)
// 2. Render to HTML
const html = renderToHtml(doc)
// 3. Split into pages
const pages = splitIntoPages(html)
// 4. Convert each page to PDF
const pdfDoc = await PDFDocument.create()
for (const pageHtml of pages) {
const canvas = await renderPage(pageHtml)
const imgData = await canvasToPdfImage(canvas)
const page = pdfDoc.addPage([canvas.width, canvas.height])
page.drawImage(imgData, {
x: 0, y: 0,
width: canvas.width,
height: canvas.height
})
}
result.value = await pdfDoc.save()
converting.value = false
}
</script>
Key implementation details
1. DOCX parsing
Extract text and structure from DOCX:
async function parseDocx(arrayBuffer: ArrayBuffer) {
const zip = await JSZip.loadAsync(arrayBuffer)
const xml = await zip.file('word/document.xml').async('text')
const parser = new DOMParser()
const doc = parser.parseFromString(xml, 'text/xml')
return {
paragraphs: doc.querySelectorAll('w:p'),
tables: doc.querySelectorAll('w:tbl')
}
}
2. HTML rendering
Convert parsed content to HTML:
function renderToHtml(doc: ParsedDocx): string {
let html = '<div class="document">'
doc.paragraphs.forEach(p => {
const text = p.textContent || ''
html += `<p>${escapeHtml(text)}</p>`
})
html += '</div>'
return html
}
3. Page splitting
Detect page breaks and split content:
function splitIntoPages(html: string): string[] {
const pages = html.split(/<div[^>]*class="[^"]*page-break[^"]*"[^>]*><\/div>/)
return pages.filter(p => p.trim())
}
4. Canvas rendering
Convert HTML to canvas image:
async function renderPage(html: string): Promise<HTMLCanvasElement> {
const container = document.createElement('div')
container.innerHTML = html
document.body.appendChild(container)
const canvas = await html2canvas(container, {
scale: 2,
useCORS: true,
logging: false
})
document.body.removeChild(container)
return canvas
}
Limitations
Complex layouts
Tables, floating images, and multi-column layouts may not render correctly.
Solution: Simplify document structure or use professional tools.
Custom fonts
Fonts may not match between Word and PDF.
Solution: Use standard web-safe fonts.
Large files
Very large documents may cause memory issues.
Solution: Process in chunks or use Web Workers.
Summary
Building a browser-based Word to PDF converter involves:
- Parsing DOCX structure
- Rendering to HTML
- Splitting into pages
- Capturing as images
- Assembling into PDF
Try it at en.sotool.top/word-to-pdf.
Top comments (0)