diff --git a/README.md b/README.md
index 2059cd3..09cfae0 100644
--- a/README.md
+++ b/README.md
@@ -871,6 +871,16 @@ docker run -d -p 3000:8080 \
Both methods can be combined — the lists are merged. For the full list of tool IDs, see the [self-hosting docs](https://bentopdf.com/docs/self-hosting/docker#disabling-specific-tools).
+You can also disable specific features inside the PDF Editor (e.g., redaction, forms) without disabling the entire editor. Add `editorDisabledCategories` to your `config.json`:
+
+```json
+{
+ "editorDisabledCategories": ["redaction"]
+}
+```
+
+For the full list of editor categories, see the [self-hosting docs](https://bentopdf.com/docs/self-hosting/docker#disabling-editor-features).
+
### 🔒 Security Features
BentoPDF runs as a non-root user using nginx-unprivileged for enhanced security:
diff --git a/docs/self-hosting/docker.md b/docs/self-hosting/docker.md
index e062a07..f983594 100644
--- a/docs/self-hosting/docker.md
+++ b/docs/self-hosting/docker.md
@@ -204,6 +204,45 @@ services:
Both methods can be combined — the lists are merged. If a tool appears in either, it is disabled.
+#### Disabling Editor Features
+
+You can also disable specific features inside the PDF Editor (e.g., redaction, annotations, forms) without disabling the entire editor tool. Add `editorDisabledCategories` to your `config.json`:
+
+```json
+{
+ "editorDisabledCategories": ["redaction", "annotation-stamp"]
+}
+```
+
+
+Full list of editor categories
+
+**Zoom:** `zoom`, `zoom-in`, `zoom-out`, `zoom-fit-page`, `zoom-fit-width`, `zoom-marquee`, `zoom-level`
+
+**Annotation:** `annotation`, `annotation-markup`, `annotation-highlight`, `annotation-underline`, `annotation-strikeout`, `annotation-squiggly`, `annotation-ink`, `annotation-text`, `annotation-stamp`
+
+**Shapes:** `annotation-shape`, `annotation-rectangle`, `annotation-circle`, `annotation-line`, `annotation-arrow`, `annotation-polygon`, `annotation-polyline`
+
+**Form:** `form`, `form-textfield`, `form-checkbox`, `form-radio`, `form-select`, `form-listbox`, `form-fill-mode`
+
+**Redaction:** `redaction`, `redaction-area`, `redaction-text`, `redaction-apply`, `redaction-clear`
+
+**Document:** `document`, `document-open`, `document-close`, `document-print`, `document-capture`, `document-export`, `document-fullscreen`, `document-protect`
+
+**Page:** `page`, `spread`, `rotate`, `scroll`, `navigation`
+
+**Panel:** `panel`, `panel-sidebar`, `panel-search`, `panel-comment`
+
+**Tools:** `tools`, `pan`, `pointer`, `capture`
+
+**Selection:** `selection`, `selection-copy`
+
+**History:** `history`, `history-undo`, `history-redo`
+
+
+
+Categories are hierarchical — disabling a parent (e.g., `annotation`) disables all its children.
+
### Custom WASM URLs (Air-Gapped / Self-Hosted)
> [!IMPORTANT]
diff --git a/docs/self-hosting/index.md b/docs/self-hosting/index.md
index d1d1f79..707a1f5 100644
--- a/docs/self-hosting/index.md
+++ b/docs/self-hosting/index.md
@@ -164,7 +164,9 @@ docker run -d -p 3000:8080 \
ghcr.io/alam00000/bentopdf:latest
```
-Both methods can be combined — the lists are merged. See the [Docker guide](/self-hosting/docker#disabling-specific-tools) for full details.
+Both methods can be combined — the lists are merged.
+
+You can also disable specific features inside the PDF Editor (e.g., redaction) without disabling the entire tool by adding `editorDisabledCategories` to `config.json`. See the [Docker guide](/self-hosting/docker#disabling-editor-features) for the full list of categories.
## Deployment Guides
diff --git a/src/js/logic/edit-pdf-page.ts b/src/js/logic/edit-pdf-page.ts
index 1a0c986..3828392 100644
--- a/src/js/logic/edit-pdf-page.ts
+++ b/src/js/logic/edit-pdf-page.ts
@@ -4,6 +4,7 @@ import { showAlert, showLoader, hideLoader } from '../ui.js';
import { formatBytes, downloadFile } from '../utils/helpers.js';
import { makeUniqueFileKey } from '../utils/deduplicate-filename.js';
import { batchDecryptIfNeeded } from '../utils/password-prompt.js';
+import { getEditorDisabledCategories } from '../utils/disabled-tools.js';
const embedPdfWasmUrl = new URL(
'embedpdf-snippet/dist/pdfium.wasm',
@@ -130,7 +131,9 @@ async function handleFiles(files: FileList) {
pdfWrapper.classList.remove('hidden');
const { default: EmbedPDF } = await import('embedpdf-snippet');
+ const disabledCategories = getEditorDisabledCategories();
viewerInstance = EmbedPDF.init({
+ disabledCategories,
type: 'container',
target: pdfContainer,
worker: true,
diff --git a/src/js/types/config-types.ts b/src/js/types/config-types.ts
index 40c29c5..191df36 100644
--- a/src/js/types/config-types.ts
+++ b/src/js/types/config-types.ts
@@ -1,3 +1,4 @@
export interface AppConfig {
disabledTools?: string[];
+ editorDisabledCategories?: string[];
}
diff --git a/src/js/utils/disabled-tools.ts b/src/js/utils/disabled-tools.ts
index a70491d..dfbc704 100644
--- a/src/js/utils/disabled-tools.ts
+++ b/src/js/utils/disabled-tools.ts
@@ -2,6 +2,7 @@ import type { AppConfig } from '@/types';
const disabledToolsSet = new Set(__DISABLED_TOOLS__);
let runtimeConfigLoaded = false;
+let editorDisabledCategories: string[] = [];
export async function loadRuntimeConfig(): Promise {
if (runtimeConfigLoaded) return;
@@ -21,6 +22,11 @@ export async function loadRuntimeConfig(): Promise {
}
}
}
+ if (Array.isArray(config.editorDisabledCategories)) {
+ editorDisabledCategories = config.editorDisabledCategories.filter(
+ (c): c is string => typeof c === 'string'
+ );
+ }
} catch {}
}
@@ -36,6 +42,10 @@ export function getToolIdFromPath(): string | null {
return withoutExt?.[1] ?? null;
}
+export function getEditorDisabledCategories(): string[] {
+ return editorDisabledCategories;
+}
+
export function isCurrentPageDisabled(): boolean {
const toolId = getToolIdFromPath();
if (!toolId) return false;