feat: make Docker container port configurable via PORT env var
fix: preserve original filename when downloading processed files
This commit is contained in:
@@ -81,6 +81,7 @@ ARG BASE_URL
|
||||
|
||||
# Set this to "true" to disable Nginx listening on IPv6
|
||||
ENV DISABLE_IPV6=false
|
||||
ENV PORT=8080
|
||||
|
||||
USER root
|
||||
RUN apk upgrade --no-cache
|
||||
|
||||
@@ -74,6 +74,7 @@ ARG BASE_URL
|
||||
ENV PUID=1000
|
||||
ENV PGID=1000
|
||||
ENV DISABLE_IPV6=false
|
||||
ENV PORT=8080
|
||||
|
||||
RUN apk upgrade --no-cache && apk add --no-cache su-exec
|
||||
|
||||
|
||||
14
README.md
14
README.md
@@ -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:
|
||||
|
||||
- **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
|
||||
|
||||
#### Basic Usage
|
||||
@@ -900,6 +900,18 @@ docker build -t 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)
|
||||
|
||||
For environments that require running as a specific non-root user (e.g., NAS devices, Kubernetes with security contexts), use the non-root Dockerfile:
|
||||
|
||||
@@ -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).
|
||||
|
||||
## 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)
|
||||
|
||||
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` |
|
||||
| `PGID` | Group ID to run as | `1000` |
|
||||
| `PORT` | Nginx listen port | `8080` |
|
||||
| `DISABLE_IPV6` | Disable IPv6 listener | `false` |
|
||||
|
||||
### Docker Compose
|
||||
|
||||
@@ -34,6 +34,12 @@ chown -R "$PUID:$PGID" \
|
||||
/usr/share/nginx/html \
|
||||
/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
|
||||
echo "Disabling Nginx IPv6 listener"
|
||||
sed -i '/^[[:space:]]*listen[[:space:]]*\[::\]:[0-9]*/s/^/#/' /etc/nginx/nginx.conf
|
||||
|
||||
@@ -9,6 +9,12 @@ entrypoint_log() {
|
||||
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
|
||||
entrypoint_log "Disabling the Nginx IPv6 listener"
|
||||
sed -i '/^[[:space:]]*listen[[:space:]]*\[::\]:[0-9]*/s/^/#/' /etc/nginx/nginx.conf
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "اتصل بنا",
|
||||
"licensing": "الترخيص",
|
||||
"allTools": "جميع الأدوات",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "فتح القائمة الرئيسية",
|
||||
"language": "اللغة"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Кантакты",
|
||||
"licensing": "Ліцэнзія",
|
||||
"allTools": "Усе інструменты",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Адкрыць галоўнае меню",
|
||||
"language": "Мова"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Kontakt",
|
||||
"licensing": "licensing",
|
||||
"allTools": "Alle værktøjer",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Åbn hovedmenu",
|
||||
"language": "Sprog"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Kontakt",
|
||||
"licensing": "Lizenzierung",
|
||||
"allTools": "Alle Werkzeuge",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Hauptmenü öffnen",
|
||||
"language": "Sprache"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Contact",
|
||||
"licensing": "Licensing",
|
||||
"allTools": "All Tools",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Open main menu",
|
||||
"language": "Language"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Contacto",
|
||||
"licensing": "Licencias",
|
||||
"allTools": "Todas las Herramientas",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Abrir menú principal",
|
||||
"language": "Idioma"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Contact",
|
||||
"licensing": "Licence",
|
||||
"allTools": "Tous les outils",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Ouvrir le menu principal",
|
||||
"language": "Langue"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Kontak",
|
||||
"licensing": "Lisensi",
|
||||
"allTools": "Semua Alat",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Buka menu utama",
|
||||
"language": "Bahasa"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Contatti",
|
||||
"licensing": "Licenze",
|
||||
"allTools": "Tutti gli strumenti",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Apri il menu principale",
|
||||
"language": "Lingua"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "문의",
|
||||
"licensing": "라이선스",
|
||||
"allTools": "모든 도구",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "메인 메뉴 열기",
|
||||
"language": "언어"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Contact",
|
||||
"licensing": "Licentie",
|
||||
"allTools": "Alle Tools",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Hoofdmenu openen",
|
||||
"language": "Taal"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Contato",
|
||||
"licensing": "Licenciamento",
|
||||
"allTools": "Todas as Ferramentas",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Abrir menu principal",
|
||||
"language": "Idioma"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Контакты",
|
||||
"licensing": "Лицензия",
|
||||
"allTools": "Все инструменты",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Открыть главное меню",
|
||||
"language": "Язык"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Kontakt",
|
||||
"licensing": "Licensiering",
|
||||
"allTools": "Alla verktyg",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Öppna huvudmenyn",
|
||||
"language": "Språk"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "İletişim",
|
||||
"licensing": "Lisanslama",
|
||||
"allTools": "Tüm Araçlar",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Ana menüyü aç",
|
||||
"language": "Dil"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "Liên hệ",
|
||||
"licensing": "Giấy phép",
|
||||
"allTools": "Tất cả công cụ",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "Mở menu chính",
|
||||
"language": "Ngôn ngữ"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "聯絡我們",
|
||||
"licensing": "產品授權",
|
||||
"allTools": "所有工具",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "開啟主選單",
|
||||
"language": "語言"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"contact": "联系我们",
|
||||
"licensing": "许可",
|
||||
"allTools": "所有工具",
|
||||
"docs": "Docs",
|
||||
"openMainMenu": "打开主菜单",
|
||||
"language": "语言"
|
||||
},
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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!');
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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.');
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -461,7 +461,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
new Blob([new Uint8Array(pageState.searchablePdfBytes)], {
|
||||
type: 'application/pdf',
|
||||
}),
|
||||
'searchable.pdf'
|
||||
pageState.file?.name || 'document.pdf'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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.');
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
>Licensing</a
|
||||
>
|
||||
<a
|
||||
href="index.html#tools-header"
|
||||
href="https://bentopdf.com/docs/"
|
||||
class="nav-link"
|
||||
data-i18n="nav.allTools"
|
||||
>All Tools</a
|
||||
data-i18n="nav.docs"
|
||||
>Docs</a
|
||||
>
|
||||
<a
|
||||
href="https://github.com/alam00000/bentopdf/"
|
||||
@@ -145,10 +145,10 @@
|
||||
>Licensing</a
|
||||
>
|
||||
<a
|
||||
href="index.html#tools-header"
|
||||
href="https://bentopdf.com/docs/"
|
||||
class="mobile-nav-link"
|
||||
data-i18n="nav.allTools"
|
||||
>All Tools</a
|
||||
data-i18n="nav.docs"
|
||||
>Docs</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user