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

@@ -81,6 +81,7 @@ ARG BASE_URL
# Set this to "true" to disable Nginx listening on IPv6 # Set this to "true" to disable Nginx listening on IPv6
ENV DISABLE_IPV6=false ENV DISABLE_IPV6=false
ENV PORT=8080
USER root USER root
RUN apk upgrade --no-cache RUN apk upgrade --no-cache

View File

@@ -74,6 +74,7 @@ ARG BASE_URL
ENV PUID=1000 ENV PUID=1000
ENV PGID=1000 ENV PGID=1000
ENV DISABLE_IPV6=false ENV DISABLE_IPV6=false
ENV PORT=8080
RUN apk upgrade --no-cache && apk add --no-cache su-exec RUN apk upgrade --no-cache && apk add --no-cache su-exec

View File

@@ -890,7 +890,7 @@ For the full list of editor categories, see the [self-hosting docs](https://bent
BentoPDF runs as a non-root user using nginx-unprivileged for enhanced security: BentoPDF runs as a non-root user using nginx-unprivileged for enhanced security:
- **Non-Root Execution**: Container runs with minimal privileges using nginx-unprivileged - **Non-Root Execution**: Container runs with minimal privileges using nginx-unprivileged
- **Port 8080**: Uses high port number to avoid requiring root privileges - **Port 8080**: Uses high port number to avoid requiring root privileges (configurable via `PORT` env var)
- **Security Best Practices**: Follows Principle of Least Privilege - **Security Best Practices**: Follows Principle of Least Privilege
#### Basic Usage #### Basic Usage
@@ -900,6 +900,18 @@ docker build -t bentopdf .
docker run -p 8080:8080 bentopdf docker run -p 8080:8080 bentopdf
``` ```
#### Custom Port
By default, BentoPDF listens on port `8080` inside the container. To change this, set the `PORT` environment variable:
```bash
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf:latest
```
| Variable | Description | Default |
| -------- | ------------------------------ | ------- |
| `PORT` | Nginx listen port in container | `8080` |
#### Custom User ID (PUID/PGID) #### Custom User ID (PUID/PGID)
For environments that require running as a specific non-root user (e.g., NAS devices, Kubernetes with security contexts), use the non-root Dockerfile: For environments that require running as a specific non-root user (e.g., NAS devices, Kubernetes with security contexts), use the non-root Dockerfile:

View File

@@ -307,6 +307,20 @@ Use the codes printed by `bash scripts/prepare-airgap.sh --list-ocr-languages`,
Set a variable to empty string to disable that module (users must configure manually via Advanced Settings). Set a variable to empty string to disable that module (users must configure manually via Advanced Settings).
## Custom Port
By default, BentoPDF listens on port `8080` inside the container. To change this, set the `PORT` environment variable at runtime:
```bash
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf:latest
```
| Variable | Description | Default |
| -------- | ------------------------------ | ------- |
| `PORT` | Nginx listen port in container | `8080` |
This works with both the standard and nonroot Dockerfiles.
## Custom User ID (PUID/PGID) ## Custom User ID (PUID/PGID)
For environments that require running as a specific non-root user (NAS devices, Kubernetes with security contexts, organizational policies), BentoPDF provides a separate Dockerfile with LSIO-style PUID/PGID support. For environments that require running as a specific non-root user (NAS devices, Kubernetes with security contexts, organizational policies), BentoPDF provides a separate Dockerfile with LSIO-style PUID/PGID support.
@@ -333,6 +347,7 @@ docker run -d \
| -------------- | --------------------- | ------- | | -------------- | --------------------- | ------- |
| `PUID` | User ID to run as | `1000` | | `PUID` | User ID to run as | `1000` |
| `PGID` | Group ID to run as | `1000` | | `PGID` | Group ID to run as | `1000` |
| `PORT` | Nginx listen port | `8080` |
| `DISABLE_IPV6` | Disable IPv6 listener | `false` | | `DISABLE_IPV6` | Disable IPv6 listener | `false` |
### Docker Compose ### Docker Compose

View File

@@ -34,6 +34,12 @@ chown -R "$PUID:$PGID" \
/usr/share/nginx/html \ /usr/share/nginx/html \
/etc/nginx/nginx.conf /etc/nginx/nginx.conf
PORT=${PORT:-8080}
if [ "$PORT" != "8080" ]; then
echo "Changing Nginx listen port to $PORT"
sed -i "s/listen 8080/listen $PORT/g; s/listen \[::\]:8080/listen [::]:$PORT/g" /etc/nginx/nginx.conf
fi
if [ "$DISABLE_IPV6" = "true" ]; then if [ "$DISABLE_IPV6" = "true" ]; then
echo "Disabling Nginx IPv6 listener" echo "Disabling Nginx IPv6 listener"
sed -i '/^[[:space:]]*listen[[:space:]]*\[::\]:[0-9]*/s/^/#/' /etc/nginx/nginx.conf sed -i '/^[[:space:]]*listen[[:space:]]*\[::\]:[0-9]*/s/^/#/' /etc/nginx/nginx.conf

View File

@@ -9,6 +9,12 @@ entrypoint_log() {
fi fi
} }
PORT=${PORT:-8080}
if [ "$PORT" != "8080" ]; then
entrypoint_log "Changing Nginx listen port to $PORT"
sed -i "s/listen 8080/listen $PORT/g; s/listen \[::\]:8080/listen [::]:$PORT/g" /etc/nginx/nginx.conf
fi
if [ "$DISABLE_IPV6" = "true" ]; then if [ "$DISABLE_IPV6" = "true" ]; then
entrypoint_log "Disabling the Nginx IPv6 listener" entrypoint_log "Disabling the Nginx IPv6 listener"
sed -i '/^[[:space:]]*listen[[:space:]]*\[::\]:[0-9]*/s/^/#/' /etc/nginx/nginx.conf sed -i '/^[[:space:]]*listen[[:space:]]*\[::\]:[0-9]*/s/^/#/' /etc/nginx/nginx.conf

View File

@@ -5,6 +5,7 @@
"contact": "اتصل بنا", "contact": "اتصل بنا",
"licensing": "الترخيص", "licensing": "الترخيص",
"allTools": "جميع الأدوات", "allTools": "جميع الأدوات",
"docs": "Docs",
"openMainMenu": "فتح القائمة الرئيسية", "openMainMenu": "فتح القائمة الرئيسية",
"language": "اللغة" "language": "اللغة"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Кантакты", "contact": "Кантакты",
"licensing": "Ліцэнзія", "licensing": "Ліцэнзія",
"allTools": "Усе інструменты", "allTools": "Усе інструменты",
"docs": "Docs",
"openMainMenu": "Адкрыць галоўнае меню", "openMainMenu": "Адкрыць галоўнае меню",
"language": "Мова" "language": "Мова"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Kontakt", "contact": "Kontakt",
"licensing": "licensing", "licensing": "licensing",
"allTools": "Alle værktøjer", "allTools": "Alle værktøjer",
"docs": "Docs",
"openMainMenu": "Åbn hovedmenu", "openMainMenu": "Åbn hovedmenu",
"language": "Sprog" "language": "Sprog"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Kontakt", "contact": "Kontakt",
"licensing": "Lizenzierung", "licensing": "Lizenzierung",
"allTools": "Alle Werkzeuge", "allTools": "Alle Werkzeuge",
"docs": "Docs",
"openMainMenu": "Hauptmenü öffnen", "openMainMenu": "Hauptmenü öffnen",
"language": "Sprache" "language": "Sprache"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Contact", "contact": "Contact",
"licensing": "Licensing", "licensing": "Licensing",
"allTools": "All Tools", "allTools": "All Tools",
"docs": "Docs",
"openMainMenu": "Open main menu", "openMainMenu": "Open main menu",
"language": "Language" "language": "Language"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Contacto", "contact": "Contacto",
"licensing": "Licencias", "licensing": "Licencias",
"allTools": "Todas las Herramientas", "allTools": "Todas las Herramientas",
"docs": "Docs",
"openMainMenu": "Abrir menú principal", "openMainMenu": "Abrir menú principal",
"language": "Idioma" "language": "Idioma"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Contact", "contact": "Contact",
"licensing": "Licence", "licensing": "Licence",
"allTools": "Tous les outils", "allTools": "Tous les outils",
"docs": "Docs",
"openMainMenu": "Ouvrir le menu principal", "openMainMenu": "Ouvrir le menu principal",
"language": "Langue" "language": "Langue"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Kontak", "contact": "Kontak",
"licensing": "Lisensi", "licensing": "Lisensi",
"allTools": "Semua Alat", "allTools": "Semua Alat",
"docs": "Docs",
"openMainMenu": "Buka menu utama", "openMainMenu": "Buka menu utama",
"language": "Bahasa" "language": "Bahasa"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Contatti", "contact": "Contatti",
"licensing": "Licenze", "licensing": "Licenze",
"allTools": "Tutti gli strumenti", "allTools": "Tutti gli strumenti",
"docs": "Docs",
"openMainMenu": "Apri il menu principale", "openMainMenu": "Apri il menu principale",
"language": "Lingua" "language": "Lingua"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "문의", "contact": "문의",
"licensing": "라이선스", "licensing": "라이선스",
"allTools": "모든 도구", "allTools": "모든 도구",
"docs": "Docs",
"openMainMenu": "메인 메뉴 열기", "openMainMenu": "메인 메뉴 열기",
"language": "언어" "language": "언어"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Contact", "contact": "Contact",
"licensing": "Licentie", "licensing": "Licentie",
"allTools": "Alle Tools", "allTools": "Alle Tools",
"docs": "Docs",
"openMainMenu": "Hoofdmenu openen", "openMainMenu": "Hoofdmenu openen",
"language": "Taal" "language": "Taal"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Contato", "contact": "Contato",
"licensing": "Licenciamento", "licensing": "Licenciamento",
"allTools": "Todas as Ferramentas", "allTools": "Todas as Ferramentas",
"docs": "Docs",
"openMainMenu": "Abrir menu principal", "openMainMenu": "Abrir menu principal",
"language": "Idioma" "language": "Idioma"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Контакты", "contact": "Контакты",
"licensing": "Лицензия", "licensing": "Лицензия",
"allTools": "Все инструменты", "allTools": "Все инструменты",
"docs": "Docs",
"openMainMenu": "Открыть главное меню", "openMainMenu": "Открыть главное меню",
"language": "Язык" "language": "Язык"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Kontakt", "contact": "Kontakt",
"licensing": "Licensiering", "licensing": "Licensiering",
"allTools": "Alla verktyg", "allTools": "Alla verktyg",
"docs": "Docs",
"openMainMenu": "Öppna huvudmenyn", "openMainMenu": "Öppna huvudmenyn",
"language": "Språk" "language": "Språk"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "İletişim", "contact": "İletişim",
"licensing": "Lisanslama", "licensing": "Lisanslama",
"allTools": "Tüm Araçlar", "allTools": "Tüm Araçlar",
"docs": "Docs",
"openMainMenu": "Ana menüyü aç", "openMainMenu": "Ana menüyü aç",
"language": "Dil" "language": "Dil"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "Liên hệ", "contact": "Liên hệ",
"licensing": "Giấy phép", "licensing": "Giấy phép",
"allTools": "Tất cả công cụ", "allTools": "Tất cả công cụ",
"docs": "Docs",
"openMainMenu": "Mở menu chính", "openMainMenu": "Mở menu chính",
"language": "Ngôn ngữ" "language": "Ngôn ngữ"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "聯絡我們", "contact": "聯絡我們",
"licensing": "產品授權", "licensing": "產品授權",
"allTools": "所有工具", "allTools": "所有工具",
"docs": "Docs",
"openMainMenu": "開啟主選單", "openMainMenu": "開啟主選單",
"language": "語言" "language": "語言"
}, },

View File

@@ -5,6 +5,7 @@
"contact": "联系我们", "contact": "联系我们",
"licensing": "许可", "licensing": "许可",
"allTools": "所有工具", "allTools": "所有工具",
"docs": "Docs",
"openMainMenu": "打开主菜单", "openMainMenu": "打开主菜单",
"language": "语言" "language": "语言"
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -461,7 +461,7 @@ document.addEventListener('DOMContentLoaded', function () {
new Blob([new Uint8Array(pageState.searchablePdfBytes)], { new Blob([new Uint8Array(pageState.searchablePdfBytes)], {
type: 'application/pdf', 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)); copiedPages.forEach((page) => newPdf.addPage(page));
const pdfBytes = await newPdf.save(); const pdfBytes = await newPdf.save();
const baseName = organizeState.file?.name.replace('.pdf', '') || 'document';
downloadFile( downloadFile(
new Blob([pdfBytes as BlobPart], { type: 'application/pdf' }), new Blob([pdfBytes as BlobPart], { type: 'application/pdf' }),
`${baseName}_organized.pdf` organizeState.file?.name || 'document.pdf'
); );
hideLoader(); hideLoader();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -310,7 +310,7 @@ async function posterize() {
const newPdfBytes = await newDoc.save(); const newPdfBytes = await newDoc.save();
downloadFile( downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }), new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
'posterized.pdf' pageState.file?.name || 'document.pdf'
); );
showAlert('Success', 'Your PDF has been posterized.'); 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, file.name);
downloadFile(rasterizedBlob, outName);
hideLoader(); hideLoader();
showAlert( showAlert(
@@ -177,9 +176,7 @@ document.addEventListener('DOMContentLoaded', () => {
quality: 95, quality: 95,
}); });
const outName = const zipEntryName = deduplicateFileName(file.name, usedNames);
file.name.replace(/\.pdf$/i, '') + '_rasterized.pdf';
const zipEntryName = deduplicateFileName(outName, usedNames);
zip.file(zipEntryName, rasterizedBlob); zip.file(zipEntryName, rasterizedBlob);
completed++; completed++;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,10 +30,10 @@
>Licensing</a >Licensing</a
> >
<a <a
href="index.html#tools-header" href="https://bentopdf.com/docs/"
class="nav-link" class="nav-link"
data-i18n="nav.allTools" data-i18n="nav.docs"
>All Tools</a >Docs</a
> >
<a <a
href="https://github.com/alam00000/bentopdf/" href="https://github.com/alam00000/bentopdf/"
@@ -145,10 +145,10 @@
>Licensing</a >Licensing</a
> >
<a <a
href="index.html#tools-header" href="https://bentopdf.com/docs/"
class="mobile-nav-link" class="mobile-nav-link"
data-i18n="nav.allTools" data-i18n="nav.docs"
>All Tools</a >Docs</a
> >
</div> </div>
</div> </div>