Files
bentopdf/public/pdfjs-viewer/form-viewer.html
abdullahalam123 c31704eb0e feat(form-filler): enable XFA support and use original file bytes
Use original file bytes instead of saved PDF doc to preserve XFA streams. This ensures PDF.js can fully render XFA-based forms by also enabling the XFA flag in the viewer.
2025-11-14 13:24:50 +05:30

276 lines
7.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html dir="ltr" mozdisallowselectionprint>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate">
<title>PDF Form Filler - Bento PDF</title>
<link rel="stylesheet" href="pdf_viewer.css">
<link rel="stylesheet" href="viewer.css">
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: #525252;
}
#viewerContainer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
background-color: #404040;
}
.toolbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 48px;
background-color: #323639;
box-shadow: 0 1px 4px rgba(0,0,0,0.3);
display: flex;
align-items: center;
padding: 0 10px;
z-index: 1000;
gap: 10px;
}
.toolbar button {
background-color: rgba(255,255,255,0.1);
border: none;
color: #fff;
padding: 8px 12px;
cursor: pointer;
border-radius: 4px;
font-size: 13px;
}
.toolbar button:hover {
background-color: rgba(255,255,255,0.2);
}
.toolbar button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.toolbar .page-info {
color: #fff;
font-size: 13px;
margin: 0 10px;
}
.toolbar input[type="number"] {
width: 60px;
padding: 4px 8px;
background-color: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
color: #fff;
border-radius: 4px;
text-align: center;
}
#viewerContainer {
top: 48px;
}
#viewer {
padding-top: 20px;
}
.page {
margin: 10px auto;
border: 1px solid #999;
box-shadow: 0 4px 10px rgba(0,0,0,0.5);
}
.toolbar .spacer {
flex: 1;
}
.toolbar .zoom-controls {
display: flex;
gap: 5px;
align-items: center;
}
.toolbar select {
background-color: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
color: #fff;
padding: 6px 10px;
border-radius: 4px;
cursor: pointer;
}
.toolbar select option {
background-color: #323639;
color: #fff;
}
</style>
</head>
<body>
<div class="toolbar">
<button id="previousPage" title="Previous Page"></button>
<input type="number" id="pageNumber" min="1" value="1">
<span class="page-info">/ <span id="numPages">--</span></span>
<button id="nextPage" title="Next Page"></button>
<div class="spacer"></div>
<div class="zoom-controls">
<button id="zoomOut" title="Zoom Out"></button>
<select id="scaleSelect">
<option value="auto">Auto</option>
<option value="page-fit">Fit Page</option>
<option value="page-width">Fit Width</option>
<option value="0.5">50%</option>
<option value="0.75">75%</option>
<option value="1" selected>100%</option>
<option value="1.25">125%</option>
<option value="1.5">150%</option>
<option value="2">200%</option>
</select>
<button id="zoomIn" title="Zoom In">+</button>
</div>
<button id="download" title="Save & Download">Download</button>
</div>
<div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
<script type="module">
import * as pdfjsLib from './pdf.mjs';
import { EventBus, PDFViewer, PDFLinkService } from './pdf_viewer.mjs';
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdf.worker.mjs';
const eventBus = new EventBus();
const linkService = new PDFLinkService({ eventBus });
const pdfViewer = new PDFViewer({
container: document.getElementById('viewerContainer'),
viewer: document.getElementById('viewer'),
eventBus,
linkService,
enableScripting: true,
enableXfa: true,
renderer: 'canvas'
});
linkService.setViewer(pdfViewer);
let pdfDocument = null;
let currentScale = 1.0;
// Listen for messages from parent window
window.addEventListener('message', async (event) => {
if (event.data.type === 'loadPDF') {
const { data } = event.data;
loadPDF(data);
} else if (event.data.type === 'getData') {
const pdfData = await getPDFData();
window.parent.postMessage({ type: 'pdfData', data: pdfData }, '*');
}
});
async function loadPDF(data) {
try {
const loadingTask = pdfjsLib.getDocument({
data,
enableXfa: true,
});
pdfDocument = await loadingTask.promise;
pdfViewer.setDocument(pdfDocument);
linkService.setDocument(pdfDocument);
document.getElementById('numPages').textContent = pdfDocument.numPages;
document.getElementById('pageNumber').max = pdfDocument.numPages;
// Notify parent that PDF is loaded
window.parent.postMessage({ type: 'pdfLoaded', numPages: pdfDocument.numPages }, '*');
} catch (error) {
console.error('Error loading PDF:', error);
window.parent.postMessage({ type: 'error', message: error.message }, '*');
}
}
async function getPDFData() {
if (!pdfDocument) return null;
// Get the PDF data with form fields filled
const data = await pdfDocument.saveDocument();
return data;
}
// Navigation controls
document.getElementById('previousPage').addEventListener('click', () => {
if (pdfViewer.currentPageNumber > 1) {
pdfViewer.currentPageNumber--;
}
});
document.getElementById('nextPage').addEventListener('click', () => {
if (pdfViewer.currentPageNumber < pdfDocument?.numPages) {
pdfViewer.currentPageNumber++;
}
});
document.getElementById('pageNumber').addEventListener('change', (e) => {
const pageNum = parseInt(e.target.value);
if (pageNum >= 1 && pageNum <= pdfDocument?.numPages) {
pdfViewer.currentPageNumber = pageNum;
}
});
// Zoom controls
document.getElementById('zoomIn').addEventListener('click', () => {
currentScale = Math.min(currentScale + 0.1, 3.0);
pdfViewer.currentScale = currentScale;
document.getElementById('scaleSelect').value = currentScale.toFixed(2);
});
document.getElementById('zoomOut').addEventListener('click', () => {
currentScale = Math.max(currentScale - 0.1, 0.1);
pdfViewer.currentScale = currentScale;
document.getElementById('scaleSelect').value = currentScale.toFixed(2);
});
document.getElementById('scaleSelect').addEventListener('change', (e) => {
const value = e.target.value;
if (value === 'auto' || value === 'page-fit' || value === 'page-width') {
pdfViewer.currentScaleValue = value;
} else {
currentScale = parseFloat(value);
pdfViewer.currentScale = currentScale;
}
});
// Download button
document.getElementById('download').addEventListener('click', async () => {
const data = await getPDFData();
if (data) {
window.parent.postMessage({ type: 'downloadPDF', data: Array.from(data) }, '*');
}
});
// Update page number display when page changes
eventBus.on('pagechanging', (evt) => {
document.getElementById('pageNumber').value = evt.pageNumber;
document.getElementById('previousPage').disabled = evt.pageNumber <= 1;
document.getElementById('nextPage').disabled = evt.pageNumber >= pdfDocument?.numPages;
});
// Notify parent that viewer is ready
window.parent.postMessage({ type: 'viewerReady' }, '*');
</script>
</body>
</html>