fix(dees-pdf-viewer): use in-memory PDF data for download and print; add robust print wrapper, cleanup and error handling
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-01-29 - 3.41.3 - fix(dees-pdf-viewer)
|
||||
use in-memory PDF data for download and print; add robust print wrapper, cleanup and error handling
|
||||
|
||||
- Download and Print now use pdfDocument.getData() to create Blob URLs so in-memory PDFs (pdf.js) can be saved/printed.
|
||||
- Print flow now opens an HTML wrapper with an iframe to allow onafterprint handling, auto-close, popup-fallback and timed cleanup of Blob URLs.
|
||||
- Added try/catch logging, URL.revokeObjectURL calls and safety timeouts to avoid resource leaks.
|
||||
- Removed context menu items that relied on the raw PDF URL (Open in New Tab, Copy PDF URL); Download/Print actions now await the async handlers.
|
||||
|
||||
## 2026-01-28 - 3.41.2 - fix(dees-pdf-viewer)
|
||||
account for devicePixelRatio when setting canvas dimensions and scale 2D context to render crisp PDF pages and thumbnails on high-DPI displays
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-catalog',
|
||||
version: '3.41.2',
|
||||
version: '3.41.3',
|
||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||
}
|
||||
|
||||
@@ -935,15 +935,98 @@ export class DeesPdfViewer extends DeesElement {
|
||||
});
|
||||
}
|
||||
|
||||
private downloadPdf() {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.pdfUrl;
|
||||
link.download = this.pdfUrl.split('/').pop() || 'document.pdf';
|
||||
link.click();
|
||||
private async downloadPdf() {
|
||||
if (!this.pdfDocument) return;
|
||||
|
||||
try {
|
||||
// Get raw PDF data from the loaded document
|
||||
const data = await this.pdfDocument.getData();
|
||||
const blob = new Blob([data.buffer], { type: 'application/pdf' });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = blobUrl;
|
||||
link.download = this.pdfUrl ? this.pdfUrl.split('/').pop() || 'document.pdf' : 'document.pdf';
|
||||
link.click();
|
||||
|
||||
// Clean up blob URL after short delay
|
||||
setTimeout(() => URL.revokeObjectURL(blobUrl), 1000);
|
||||
} catch (error) {
|
||||
console.error('Error downloading PDF:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private printPdf() {
|
||||
window.open(this.pdfUrl, '_blank')?.print();
|
||||
private async printPdf() {
|
||||
if (!this.pdfDocument) return;
|
||||
|
||||
try {
|
||||
// Get raw PDF data from the loaded document
|
||||
const data = await this.pdfDocument.getData();
|
||||
const blob = new Blob([data.buffer], { type: 'application/pdf' });
|
||||
const pdfUrl = URL.createObjectURL(blob);
|
||||
|
||||
// Create an HTML wrapper page that embeds the PDF and handles print/close
|
||||
// This gives us control over the afterprint event (direct PDF URLs don't support it)
|
||||
const htmlContent = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Print PDF</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; }
|
||||
html, body { width: 100%; height: 100%; overflow: hidden; }
|
||||
iframe { width: 100%; height: 100%; border: none; }
|
||||
@media print {
|
||||
html, body, iframe { width: 100%; height: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="${pdfUrl}" type="application/pdf"></iframe>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
setTimeout(function() {
|
||||
window.focus();
|
||||
window.print();
|
||||
}, 500);
|
||||
};
|
||||
window.onafterprint = function() {
|
||||
window.close();
|
||||
};
|
||||
// Safety close after 2 minutes
|
||||
setTimeout(function() { window.close(); }, 120000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
const htmlBlob = new Blob([htmlContent], { type: 'text/html' });
|
||||
const htmlUrl = URL.createObjectURL(htmlBlob);
|
||||
|
||||
const printWindow = window.open(htmlUrl, '_blank', 'width=800,height=600');
|
||||
if (printWindow) {
|
||||
// Cleanup blob URLs when window closes
|
||||
const checkClosed = setInterval(() => {
|
||||
if (printWindow.closed) {
|
||||
clearInterval(checkClosed);
|
||||
URL.revokeObjectURL(pdfUrl);
|
||||
URL.revokeObjectURL(htmlUrl);
|
||||
}
|
||||
}, 500);
|
||||
// Safety cleanup after 2 minutes
|
||||
setTimeout(() => {
|
||||
clearInterval(checkClosed);
|
||||
URL.revokeObjectURL(pdfUrl);
|
||||
URL.revokeObjectURL(htmlUrl);
|
||||
}, 120000);
|
||||
} else {
|
||||
// Popup blocked - fall back to direct navigation
|
||||
window.open(pdfUrl, '_blank');
|
||||
setTimeout(() => URL.revokeObjectURL(pdfUrl), 60000);
|
||||
URL.revokeObjectURL(htmlUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error printing PDF:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -967,33 +1050,18 @@ export class DeesPdfViewer extends DeesElement {
|
||||
}
|
||||
|
||||
items.push(
|
||||
{
|
||||
name: 'Open PDF in New Tab',
|
||||
iconName: 'lucide:ExternalLink',
|
||||
action: async () => {
|
||||
window.open(this.pdfUrl, '_blank');
|
||||
}
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
name: 'Copy PDF URL',
|
||||
iconName: 'lucide:Link',
|
||||
action: async () => {
|
||||
await navigator.clipboard.writeText(this.pdfUrl);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Download PDF',
|
||||
iconName: 'lucide:Download',
|
||||
action: async () => {
|
||||
this.downloadPdf();
|
||||
await this.downloadPdf();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Print PDF',
|
||||
iconName: 'lucide:Printer',
|
||||
action: async () => {
|
||||
this.printPdf();
|
||||
await this.printPdf();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user