feat: make Docker container port configurable via PORT env var

fix: preserve original filename when downloading processed files
This commit is contained in:
alam00000
2026-04-05 13:44:16 +05:30
parent 2f0d48c01b
commit bfe4e5b663
77 changed files with 127 additions and 126 deletions

View File

@@ -65,11 +65,9 @@ worker.onmessage = function (e) {
if (data.status === 'success' && data.modifiedPDF !== undefined) {
hideLoader();
const originalName =
pageState.file?.name.replace(/\.pdf$/i, '') || 'document';
downloadFile(
new Blob([new Uint8Array(data.modifiedPDF)], { type: 'application/pdf' }),
`${originalName}_with_attachments.pdf`
pageState.file?.name || 'document.pdf'
);
showAlert(

View File

@@ -178,11 +178,9 @@ async function addBlankPages() {
}
const newPdfBytes = await newPdf.save();
const originalName = pageState.file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
`${originalName}_blank-pages-added.pdf`
pageState.file.name
);
showAlert(

View File

@@ -502,7 +502,7 @@ async function addPageLabels() {
downloadFile(
new Blob([outputBytes], { type: 'application/pdf' }),
'page-labels-added.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert(
translate('common.success', 'Success'),

View File

@@ -1010,7 +1010,7 @@ async function applyWatermark() {
downloadFile(
new Blob([new Uint8Array(resultBytes)], { type: 'application/pdf' }),
'watermarked.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert('Success', 'Watermark added successfully!', 'success');
} catch (e: unknown) {

View File

@@ -234,7 +234,7 @@ async function processAllPages(): Promise<void> {
const resultBytes = await newPdfDoc.save();
downloadFile(
new Blob([new Uint8Array(resultBytes)], { type: 'application/pdf' }),
'color-adjusted.pdf'
files[0]?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -140,7 +140,7 @@ async function changeBackgroundColor() {
const newPdfBytes = await newPdfDoc.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'background-changed.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -516,7 +516,7 @@ async function applyBatesNumbers() {
fileCounter++;
const pdfBytes = await pdfDoc.save();
results.push({
name: `bates_${entry.file.name}`,
name: entry.file.name,
bytes: new Uint8Array(pdfBytes),
});
}

View File

@@ -2306,7 +2306,7 @@ downloadBtn?.addEventListener('click', async () => {
const blob = new Blob([new Uint8Array(pdfBytes)], {
type: 'application/pdf',
});
downloadFile(blob, `${originalFileName}-bookmarked.pdf`);
downloadFile(blob, `${originalFileName}.pdf`);
await showAlertModal('Success', 'PDF saved successfully!');

View File

@@ -222,7 +222,7 @@ async function changePermissions() {
const blob = new Blob([new Uint8Array(outputFile)], {
type: 'application/pdf',
});
downloadFile(blob, `permissions-changed-${pageState.file.name}`);
downloadFile(blob, pageState.file.name);
if (loaderModal) loaderModal.classList.add('hidden');

View File

@@ -243,11 +243,9 @@ async function combineToSinglePage() {
}
const newPdfBytes = await newDoc.save();
const originalName = pageState.file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
`${originalName}_combined.pdf`
pageState.file.name
);
showAlert(

View File

@@ -465,10 +465,7 @@ document.addEventListener('DOMContentLoaded', () => {
const savingsPercent =
savings > 0 ? ((savings / originalFile.size) * 100).toFixed(1) : 0;
downloadFile(
resultBlob,
originalFile.name.replace(/\.pdf$/i, '') + '_compressed.pdf'
);
downloadFile(resultBlob, originalFile.name);
hideLoader();
@@ -523,8 +520,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
totalCompressedSize += resultBytes.length;
const baseName = file.name.replace(/\.pdf$/i, '');
zip.file(`${baseName}_compressed.pdf`, resultBytes);
zip.file(file.name, resultBytes);
}
const zipBlob = await zip.generateAsync({ type: 'blob' });

View File

@@ -288,10 +288,9 @@ async function performCrop() {
finalPdfBytes = await performMetadataCrop(finalCropData);
}
const fileName = isDestructive ? 'flattened_crop.pdf' : 'standard_crop.pdf';
downloadFile(
new Blob([new Uint8Array(finalPdfBytes)], { type: 'application/pdf' }),
fileName
cropperState.file?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -137,7 +137,7 @@ async function decryptPdf() {
const blob = new Blob([decryptedBytes.slice().buffer], {
type: 'application/pdf',
});
downloadFile(blob, `unlocked-${file.name}`);
downloadFile(blob, file.name);
if (loaderModal) loaderModal.classList.add('hidden');
showAlert(
@@ -168,7 +168,7 @@ async function decryptPdf() {
password
);
zip.file(`unlocked-${file.name}`, decryptedBytes, { binary: true });
zip.file(file.name, decryptedBytes, { binary: true });
successCount++;
} catch (fileError: unknown) {
errorCount++;

View File

@@ -267,12 +267,11 @@ async function deletePages() {
new Uint8Array(srcBytes),
deleteState.pagesToDelete
);
const baseName = deleteState.file?.name.replace('.pdf', '') || 'document';
downloadFile(
new Blob([new Uint8Array(resultBytes)], {
type: 'application/pdf',
}),
`${baseName}_pages_removed.pdf`
deleteState.file?.name || 'document.pdf'
);
hideLoader();

View File

@@ -171,8 +171,7 @@ async function processDeskew(): Promise<void> {
displayResults(result);
const filename = file.name.replace('.pdf', '_deskewed.pdf');
downloadFile(resultPdf, filename);
downloadFile(resultPdf, file.name);
}
hideLoader();

View File

@@ -739,10 +739,7 @@ async function processSignature(): Promise<void> {
const blob = new Blob([signedPdfBytes.slice().buffer], {
type: 'application/pdf',
});
const originalName = state.pdfFile?.name ?? 'document.pdf';
const signedName = originalName.replace(/\.pdf$/i, '_signed.pdf');
downloadFile(blob, signedName);
downloadFile(blob, state.pdfFile?.name ?? 'document.pdf');
hideLoader();
showAlert(

View File

@@ -177,11 +177,9 @@ async function dividePages() {
}
const newPdfBytes = await newPdfDoc.save();
const originalName = pageState.file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
`${originalName}_divided.pdf`
pageState.file.name
);
showAlert(

View File

@@ -222,7 +222,7 @@ export async function processAndSave() {
const newPdfBytes = await newPdfDoc.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'organized.pdf'
state.files[0]?.name || 'document.pdf'
);
} catch (e) {
console.error('Save error:', e);

View File

@@ -60,11 +60,9 @@ worker.onmessage = function (e) {
} else if (data.status === 'success' && data.modifiedPDF !== undefined) {
hideLoader();
const originalName =
pageState.file?.name.replace(/\.pdf$/i, '') || 'document';
downloadFile(
new Blob([new Uint8Array(data.modifiedPDF)], { type: 'application/pdf' }),
`${originalName}_edited.pdf`
pageState.file?.name || 'document.pdf'
);
showAlert(

View File

@@ -351,11 +351,9 @@ async function saveMetadata() {
});
const newPdfBytes = await pageState.pdfDoc.save();
const originalName = pageState.file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
`${originalName}_metadata-edited.pdf`
pageState.file.name
);
showAlert(

View File

@@ -17,6 +17,7 @@ import type { DocManagerPlugin } from '@/types';
let viewerInstance: EmbedPdfContainer | null = null;
let docManagerPlugin: DocManagerPlugin | null = null;
let isViewerInitialized = false;
let currentFileName = 'document.pdf';
const fileEntryMap = new Map<string, HTMLElement>();
function resetViewer() {
@@ -128,6 +129,7 @@ async function handleFiles(files: FileList) {
if (!isViewerInitialized) {
const firstFile = decryptedFiles[0];
currentFileName = firstFile.name;
const firstBuffer = await firstFile.arrayBuffer();
pdfContainer.textContent = '';
@@ -217,7 +219,7 @@ async function handleFiles(files: FileList) {
const exportPlugin = registry.getPlugin('export').provides();
const arrayBuffer = await exportPlugin.saveAsCopy().toPromise();
const blob = new Blob([arrayBuffer], { type: 'application/pdf' });
downloadFile(blob, 'edited-document.pdf');
downloadFile(blob, currentFileName);
} catch (err) {
console.error('Error downloading PDF:', err);
showAlert('Error', 'Failed to download the edited PDF.');

View File

@@ -173,7 +173,7 @@ async function encryptPdf() {
const blob = new Blob([new Uint8Array(outputFile)], {
type: 'application/pdf',
});
downloadFile(blob, `encrypted-${pageState.file.name}`);
downloadFile(blob, pageState.file.name);
if (loaderModal) loaderModal.classList.add('hidden');

View File

@@ -135,7 +135,7 @@ async function fixPageSize() {
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'standardized.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -174,10 +174,7 @@ async function flattenPdf() {
}
const flattenedBytes = await pdfDoc.save();
const zipEntryName = deduplicateFileName(
`flattened_${file.name}`,
usedNames
);
const zipEntryName = deduplicateFileName(file.name, usedNames);
zip.file(zipEntryName, flattenedBytes);
processedCount++;
} catch (e) {

View File

@@ -124,8 +124,7 @@ async function processFiles() {
if (loaderText) loaderText.textContent = msg;
});
const baseName = file.name.replace(/\.pdf$/i, '');
downloadFile(resultBlob, `${baseName}_outlined.pdf`);
downloadFile(resultBlob, file.name);
if (loaderModal) loaderModal.classList.add('hidden');
} else {
if (loaderModal) loaderModal.classList.remove('hidden');
@@ -143,11 +142,7 @@ async function processFiles() {
try {
const resultBlob = await convertFileToOutlines(file, () => {});
const arrayBuffer = await resultBlob.arrayBuffer();
const baseName = file.name.replace(/\.pdf$/i, '');
const zipEntryName = deduplicateFileName(
`${baseName}_outlined.pdf`,
usedNames
);
const zipEntryName = deduplicateFileName(file.name, usedNames);
zip.file(zipEntryName, arrayBuffer);
processedCount++;
} catch (e) {

View File

@@ -65,6 +65,7 @@ let pages: PageData[] = [];
let currentPageIndex = 0;
let uploadedPdfDoc: PDFDocument | null = null;
let uploadedPdfjsDoc: PDFDocumentProxy | null = null;
let uploadedFileName: string | null = null;
let pageSize: { width: number; height: number } = { width: 612, height: 792 };
let currentScale = 1.333;
let pdfViewerOffset = { x: 0, y: 0 };
@@ -2717,7 +2718,7 @@ downloadBtn.addEventListener('click', async () => {
const blob = new Blob([new Uint8Array(pdfBytes)], {
type: 'application/pdf',
});
downloadFile(blob, 'fillable-form.pdf');
downloadFile(blob, uploadedFileName || 'document.pdf');
showModal(
'Success',
'Your PDF has been downloaded successfully.',
@@ -3141,6 +3142,7 @@ async function handlePdfUpload(file: File) {
const arrayBuffer = result.bytes;
uploadedPdfjsDoc = result.pdf;
uploadedPdfDoc = await loadPdfDocument(arrayBuffer);
uploadedFileName = file.name;
// Check for existing fields and update counter
existingFieldNames.clear();

View File

@@ -242,7 +242,7 @@ async function addHeaderFooter() {
const newPdfBytes = await pageState.pdfDoc.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'header-footer-added.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -164,7 +164,7 @@ async function invertColors() {
const newPdfBytes = await newPdfDoc.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'inverted.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert('Success', 'Colors inverted successfully!', 'success', () => {
resetState();

View File

@@ -200,11 +200,9 @@ async function nUpTool() {
}
const newPdfBytes = await newDoc.save();
const originalName = pageState.file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
`${originalName}_${n}-up.pdf`
pageState.file.name
);
showAlert(

View File

@@ -461,7 +461,7 @@ document.addEventListener('DOMContentLoaded', function () {
new Blob([new Uint8Array(pageState.searchablePdfBytes)], {
type: 'application/pdf',
}),
'searchable.pdf'
pageState.file?.name || 'document.pdf'
);
}
});

View File

@@ -380,10 +380,9 @@ async function saveChanges() {
copiedPages.forEach((page) => newPdf.addPage(page));
const pdfBytes = await newPdf.save();
const baseName = organizeState.file?.name.replace('.pdf', '') || 'document';
downloadFile(
new Blob([pdfBytes as BlobPart], { type: 'application/pdf' }),
`${baseName}_organized.pdf`
organizeState.file?.name || 'document.pdf'
);
hideLoader();

View File

@@ -177,12 +177,9 @@ async function processOverlay() {
}
const modeLabel = mode.replace('--', '');
const baseName = pageState.baseFile.name.replace(/\.pdf$/i, '');
const fileName = `${baseName}_${modeLabel}.pdf`;
downloadFile(
new Blob([new Uint8Array(outputFile)], { type: 'application/pdf' }),
fileName
pageState.baseFile.name
);
showAlert(

View File

@@ -181,7 +181,7 @@ async function addPageNumbers() {
new Blob([resultBytes as unknown as BlobPart], {
type: 'application/pdf',
}),
'paginated.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert('Success', 'Page numbers added successfully!', 'success', () => {
resetState();

View File

@@ -524,12 +524,9 @@ async function createBooklet() {
}
const pdfBytes = await outputDoc.save();
const originalName =
pageState.file?.name.replace(/\.pdf$/i, '') || 'document';
downloadFile(
new Blob([new Uint8Array(pdfBytes)], { type: 'application/pdf' }),
`${originalName}_booklet.pdf`
pageState.file?.name || 'document.pdf'
);
showAlert(

View File

@@ -423,9 +423,7 @@ document.addEventListener('DOMContentLoaded', () => {
const blob = new Blob([new Uint8Array(pdfBytes)], {
type: 'application/pdf',
});
const outName =
currentFile!.name.replace(/\.pdf$/i, '') + '_layers.pdf';
downloadFile(blob, outName);
downloadFile(blob, currentFile!.name);
hideLoader();
resetState();
showAlert('Success', 'PDF with layer changes saved!', 'success');

View File

@@ -142,7 +142,7 @@ async function convert() {
const resultBytes = await newPdfDoc.save();
downloadFile(
new Blob([new Uint8Array(resultBytes)], { type: 'application/pdf' }),
'greyscale.pdf'
files[0]?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -310,7 +310,7 @@ async function posterize() {
const newPdfBytes = await newDoc.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'posterized.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert('Success', 'Your PDF has been posterized.');

View File

@@ -146,8 +146,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
);
const outName = file.name.replace(/\.pdf$/i, '') + '_rasterized.pdf';
downloadFile(rasterizedBlob, outName);
downloadFile(rasterizedBlob, file.name);
hideLoader();
showAlert(
@@ -177,9 +176,7 @@ document.addEventListener('DOMContentLoaded', () => {
quality: 95,
});
const outName =
file.name.replace(/\.pdf$/i, '') + '_rasterized.pdf';
const zipEntryName = deduplicateFileName(outName, usedNames);
const zipEntryName = deduplicateFileName(file.name, usedNames);
zip.file(zipEntryName, rasterizedBlob);
completed++;

View File

@@ -35,7 +35,7 @@ export async function redact(redactions: RedactionRect[], canvasScale: number) {
const redactedBytes = await state.pdfDoc.save();
downloadFile(
new Blob([new Uint8Array(redactedBytes)], { type: 'application/pdf' }),
'redacted.pdf'
state.files[0]?.name || 'document.pdf'
);
} catch (e) {
console.error(e);

View File

@@ -144,7 +144,7 @@ async function processRemoveAnnotations() {
const newPdfBytes = await pageState.pdfDoc.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'annotations-removed.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert('Success', 'Annotations removed successfully!', 'success', () => {
resetState();

View File

@@ -313,7 +313,7 @@ async function processRemoveBlankPages() {
const newPdfBytes = await newPdf.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'blank-pages-removed.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -158,7 +158,7 @@ async function removeMetadata() {
const newPdfBytes = await pdfDoc.save();
downloadFile(
new Blob([newPdfBytes as BlobPart], { type: 'application/pdf' }),
'metadata-removed.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert('Success', 'Metadata removed successfully!', 'success', () => {
resetState();

View File

@@ -153,7 +153,7 @@ async function removeRestrictions() {
const blob = new Blob([new Uint8Array(outputFile)], {
type: 'application/pdf',
});
downloadFile(blob, `unrestricted-${pageState.file.name}`);
downloadFile(blob, pageState.file.name);
if (loaderModal) loaderModal.classList.add('hidden');

View File

@@ -124,20 +124,16 @@ async function reversePages() {
showLoader(`Reversing ${file.name} (${j + 1}/${validFiles.length})...`);
const newPdfBytes = await reverseSingleFile(file);
const originalName = file.name.replace(/\.pdf$/i, '');
const fileName = `${originalName}_reversed.pdf`;
const zipEntryName = deduplicateFileName(fileName, usedNames);
const zipEntryName = deduplicateFileName(file.name, usedNames);
zip.file(zipEntryName, newPdfBytes);
}
if (validFiles.length === 1) {
const file = validFiles[0];
const newPdfBytes = await reverseSingleFile(file);
const originalName = file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
`${originalName}_reversed.pdf`
file.name
);
} else {
const zipBlob = await zip.generateAsync({ type: 'blob' });

View File

@@ -319,11 +319,9 @@ async function applyRotations() {
}
const rotatedPdfBytes = await newPdfDoc.save();
const originalName = pageState.file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([new Uint8Array(rotatedPdfBytes)], { type: 'application/pdf' }),
`${originalName}_rotated.pdf`
pageState.file.name
);
showAlert(

View File

@@ -246,13 +246,11 @@ async function applyRotations() {
new Uint8Array(pdfBytes),
pageState.rotations
);
const originalName = pageState.file.name.replace(/\.pdf$/i, '');
downloadFile(
new Blob([rotatedPdfBytes as unknown as BlobPart], {
type: 'application/pdf',
}),
`${originalName}_rotated.pdf`
pageState.file.name
);
showAlert(

View File

@@ -147,7 +147,7 @@ async function runSanitize() {
downloadFile(
new Blob([new Uint8Array(result.bytes)], { type: 'application/pdf' }),
'sanitized.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -257,7 +257,7 @@ async function processAllPages(): Promise<void> {
const resultBytes = await newPdfDoc.save();
downloadFile(
new Blob([new Uint8Array(resultBytes)], { type: 'application/pdf' }),
'scanned.pdf'
files[0]?.name || 'document.pdf'
);
showAlert(
'Success',

View File

@@ -303,10 +303,7 @@ async function applyAndSaveSignatures() {
const blob = new Blob([new Uint8Array(flattenedPdfBytes)], {
type: 'application/pdf',
});
downloadFile(
blob,
`signed_flattened_${signState.file?.name || 'document.pdf'}`
);
downloadFile(blob, signState.file?.name || 'document.pdf');
hideLoader();
showAlert('Success', 'Signed PDF saved successfully!', 'success', () => {

View File

@@ -180,10 +180,7 @@ worker.onmessage = (e: MessageEvent<TOCWorkerResponse>) => {
const pdfBytes = new Uint8Array(pdfBytesBuffer);
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
downloadFile(
blob,
pdfFile?.name.replace('.pdf', '_with_toc.pdf') || 'output_with_toc.pdf'
);
downloadFile(blob, pdfFile?.name || 'document.pdf');
showStatus(
'Table of contents generated successfully! Download started.',

View File

@@ -186,7 +186,7 @@ async function changeTextColor() {
const newPdfBytes = await newPdfDoc.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'text-color-changed.pdf'
pageState.file?.name || 'document.pdf'
);
showAlert('Success', 'Text color changed successfully!', 'success', () => {
resetState();

View File

@@ -227,14 +227,10 @@ async function processTimestamp(): Promise<void> {
try {
const timestampedBytes = await timestampPdf(state.pdfBytes, tsaUrl);
const outputFilename = state.pdfFile.name.replace(
/\.pdf$/i,
'_timestamped.pdf'
);
const blob = new Blob([new Uint8Array(timestampedBytes)], {
type: 'application/pdf',
});
downloadFile(blob, outputFilename);
downloadFile(blob, state.pdfFile.name);
showAlert(
'Success',