2025-10-12 11:55:45 +05:30
|
|
|
// NOTE: This is a work in progress and does not work correctly as of yet
|
2026-04-17 23:40:24 +05:30
|
|
|
import DOMPurify from 'dompurify';
|
2025-10-12 11:55:45 +05:30
|
|
|
import { showLoader, hideLoader, showAlert } from '../ui.js';
|
|
|
|
|
import { readFileAsArrayBuffer } from '../utils/helpers.js';
|
|
|
|
|
import { state } from '../state.js';
|
|
|
|
|
|
|
|
|
|
export async function wordToPdf() {
|
2025-10-17 11:37:32 +05:30
|
|
|
const file = state.files[0];
|
|
|
|
|
if (!file) {
|
|
|
|
|
showAlert('No File', 'Please upload a .docx file first.');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
showLoader('Preparing preview...');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const mammothOptions = {
|
|
|
|
|
// @ts-expect-error TS(2304) FIXME: Cannot find name 'mammoth'.
|
2026-03-31 17:59:49 +05:30
|
|
|
convertImage: mammoth.images.inline(
|
|
|
|
|
(element: {
|
|
|
|
|
read: (encoding: string) => Promise<string>;
|
|
|
|
|
contentType: string;
|
|
|
|
|
}) => {
|
|
|
|
|
return element.read('base64').then((imageBuffer: string) => {
|
|
|
|
|
return {
|
|
|
|
|
src: `data:${element.contentType};base64,${imageBuffer}`,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
),
|
2025-10-17 11:37:32 +05:30
|
|
|
};
|
|
|
|
|
const arrayBuffer = await readFileAsArrayBuffer(file);
|
|
|
|
|
// @ts-expect-error TS(2304) FIXME: Cannot find name 'mammoth'.
|
|
|
|
|
const { value: html } = await mammoth.convertToHtml(
|
|
|
|
|
{ arrayBuffer },
|
|
|
|
|
mammothOptions
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Get references to our modal elements from index.html
|
|
|
|
|
const previewModal = document.getElementById('preview-modal');
|
|
|
|
|
const previewContent = document.getElementById('preview-content');
|
|
|
|
|
const downloadBtn = document.getElementById('preview-download-btn');
|
|
|
|
|
const closeBtn = document.getElementById('preview-close-btn');
|
|
|
|
|
|
2026-04-17 23:40:24 +05:30
|
|
|
const STYLE_ID = 'word-to-pdf-preview-style';
|
|
|
|
|
if (!document.getElementById(STYLE_ID)) {
|
|
|
|
|
const styleEl = document.createElement('style');
|
|
|
|
|
styleEl.id = STYLE_ID;
|
|
|
|
|
styleEl.textContent = `
|
|
|
|
|
#preview-content { font-family: 'Times New Roman', Times, serif; font-size: 12pt; line-height: 1.5; color: black; }
|
|
|
|
|
#preview-content table { border-collapse: collapse; width: 100%; }
|
|
|
|
|
#preview-content td, #preview-content th { border: 1px solid #dddddd; text-align: left; padding: 8px; }
|
|
|
|
|
#preview-content img { max-width: 100%; height: auto; }
|
|
|
|
|
#preview-content a { color: #0000ee; text-decoration: underline; }
|
|
|
|
|
`;
|
|
|
|
|
document.head.appendChild(styleEl);
|
|
|
|
|
}
|
|
|
|
|
previewContent.innerHTML = DOMPurify.sanitize(html);
|
2025-10-17 11:37:32 +05:30
|
|
|
|
|
|
|
|
const marginDiv = document.createElement('div');
|
|
|
|
|
marginDiv.style.height = '100px';
|
|
|
|
|
previewContent.appendChild(marginDiv);
|
|
|
|
|
|
|
|
|
|
const images = previewContent.querySelectorAll('img');
|
|
|
|
|
const imagePromises = Array.from(images).map((img) => {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
// @ts-expect-error TS(2794) FIXME: Expected 1 arguments, but got 0. Did you forget to... Remove this comment to see the full error message
|
|
|
|
|
if (img.complete) resolve();
|
|
|
|
|
else img.onload = resolve;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
await Promise.all(imagePromises);
|
|
|
|
|
|
|
|
|
|
previewModal.classList.remove('hidden');
|
|
|
|
|
hideLoader();
|
|
|
|
|
|
|
|
|
|
const downloadHandler = async () => {
|
|
|
|
|
showLoader('Generating High-Quality PDF...');
|
|
|
|
|
|
|
|
|
|
// @ts-expect-error TS(2339) FIXME: Property 'jspdf' does not exist on type 'Window & ... Remove this comment to see the full error message
|
|
|
|
|
const { jsPDF } = window.jspdf;
|
|
|
|
|
const doc = new jsPDF({
|
|
|
|
|
orientation: 'p',
|
|
|
|
|
unit: 'pt',
|
|
|
|
|
format: 'letter',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await doc.html(previewContent, {
|
2026-03-31 17:59:49 +05:30
|
|
|
callback: function (doc: {
|
|
|
|
|
internal: { pageSize: { getHeight: () => number } };
|
|
|
|
|
link: (
|
|
|
|
|
x: number,
|
|
|
|
|
y: number,
|
|
|
|
|
w: number,
|
|
|
|
|
h: number,
|
|
|
|
|
opts: { url: string }
|
|
|
|
|
) => void;
|
|
|
|
|
save: (name: string) => void;
|
|
|
|
|
setPage: (page: number) => void;
|
|
|
|
|
}) {
|
2025-10-17 11:37:32 +05:30
|
|
|
const links = previewContent.querySelectorAll('a');
|
|
|
|
|
const pageHeight = doc.internal.pageSize.getHeight();
|
|
|
|
|
const containerRect = previewContent.getBoundingClientRect(); // Get container's position
|
|
|
|
|
|
|
|
|
|
links.forEach((link) => {
|
|
|
|
|
if (!link.href) return;
|
|
|
|
|
|
|
|
|
|
const linkRect = link.getBoundingClientRect();
|
|
|
|
|
|
|
|
|
|
// Calculate position relative to the preview container's top-left
|
|
|
|
|
const relativeX = linkRect.left - containerRect.left;
|
|
|
|
|
const relativeY = linkRect.top - containerRect.top;
|
|
|
|
|
|
|
|
|
|
const pageNum = Math.floor(relativeY / pageHeight) + 1;
|
|
|
|
|
const yOnPage = relativeY % pageHeight;
|
|
|
|
|
|
|
|
|
|
doc.setPage(pageNum);
|
|
|
|
|
try {
|
|
|
|
|
doc.link(
|
|
|
|
|
relativeX + 45,
|
|
|
|
|
yOnPage + 45,
|
|
|
|
|
linkRect.width,
|
|
|
|
|
linkRect.height,
|
|
|
|
|
{ url: link.href }
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('Could not add link:', link.href, e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const outputFileName = `${file.name.replace(/\.[^/.]+$/, '')}.pdf`;
|
|
|
|
|
doc.save(outputFileName);
|
|
|
|
|
hideLoader();
|
|
|
|
|
},
|
|
|
|
|
autoPaging: 'slice',
|
|
|
|
|
x: 45,
|
|
|
|
|
y: 45,
|
|
|
|
|
width: 522,
|
|
|
|
|
windowWidth: previewContent.scrollWidth,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const closeHandler = () => {
|
|
|
|
|
previewModal.classList.add('hidden');
|
|
|
|
|
previewContent.innerHTML = '';
|
|
|
|
|
downloadBtn.removeEventListener('click', downloadHandler);
|
|
|
|
|
closeBtn.removeEventListener('click', closeHandler);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
downloadBtn.addEventListener('click', downloadHandler);
|
|
|
|
|
closeBtn.addEventListener('click', closeHandler);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
hideLoader();
|
|
|
|
|
showAlert(
|
|
|
|
|
'Preview Error',
|
|
|
|
|
`Could not generate a preview. The file may be corrupt or contain unsupported features. Error: ${e.message}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|