From 43a5f72b01be382193a19d7575f30cd5eda96e05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?=
<127139797+balazs-szucs@users.noreply.github.com>
Date: Thu, 25 Dec 2025 15:22:46 +0100
Subject: [PATCH] [V2] feat(crop): add auto-crop whitespace option to crop tool
UI (#5275)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Description of Changes
This pull request adds an auto-crop feature to the PDF crop tool,
allowing users to automatically crop whitespace from PDFs. The UI now
includes an "Auto-crop whitespace" checkbox, and when enabled, manual
crop controls are hidden. The crop operation logic and form data
submission have been updated to support this new option.
**Auto-crop Feature Implementation**
* Added an `autoCrop` boolean parameter to the `CropParameters`
interface and set its default value to `false` in
`useCropParameters.ts`.
* Updated the crop operation logic in `useCropOperation.ts` to include
the `autoCrop` parameter in the form data and only send manual crop
coordinates if `autoCrop` is disabled.
**User Interface Updates**
* Added an "Auto-crop whitespace" checkbox to the crop settings UI in
`CropSettings.tsx`, which toggles the auto-crop feature
* Modified the crop settings UI to hide manual crop controls and
validation alerts when auto-crop is enabled
**Localization**
* Added a new translation string for "Auto-crop whitespace" in the
English locale file `translation.toml`.
---
## Checklist
### General
- [X] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [X] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [X] I have performed a self-review of my own code
- [X] My changes generate no new warnings
### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### Translations (if applicable)
- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)
### UI Changes (if applicable)
- [X] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)
### Testing (if applicable)
- [X] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.
Signed-off-by: Balázs Szücs
---
.../public/locales/en-GB/translation.toml | 1 +
.../components/tools/crop/CropSettings.tsx | 134 ++++++++++--------
.../core/hooks/tools/crop/useCropOperation.ts | 16 ++-
.../hooks/tools/crop/useCropParameters.ts | 2 +
4 files changed, 86 insertions(+), 67 deletions(-)
diff --git a/frontend/public/locales/en-GB/translation.toml b/frontend/public/locales/en-GB/translation.toml
index 3b7797f9e..3bb44b13e 100644
--- a/frontend/public/locales/en-GB/translation.toml
+++ b/frontend/public/locales/en-GB/translation.toml
@@ -2968,6 +2968,7 @@ header = "Crop PDF"
submit = "Apply Crop"
noFileSelected = "Select a PDF file to begin cropping"
reset = "Reset to full PDF"
+autoCrop = "Auto-crop whitespace"
[crop.preview]
title = "Crop Area Selection"
diff --git a/frontend/src/core/components/tools/crop/CropSettings.tsx b/frontend/src/core/components/tools/crop/CropSettings.tsx
index fae3afe57..441186e03 100644
--- a/frontend/src/core/components/tools/crop/CropSettings.tsx
+++ b/frontend/src/core/components/tools/crop/CropSettings.tsx
@@ -1,5 +1,5 @@
import { useMemo, useState, useEffect } from "react";
-import { Stack, Text, Box, Group, ActionIcon, Center, Alert } from "@mantine/core";
+import { Stack, Text, Box, Group, ActionIcon, Center, Alert, Checkbox } from "@mantine/core";
import { useTranslation } from "react-i18next";
import RestartAltIcon from "@mui/icons-material/RestartAlt";
import { CropParametersHook } from "@app/hooks/tools/crop/useCropParameters";
@@ -151,69 +151,81 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
return (
- {/* PDF Preview with Crop Selector */}
-
-
-
- {t("crop.preview.title", "Crop Area Selection")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Manual Coordinate Input */}
- parameters.updateParameter('autoCrop', e.currentTarget.checked)}
disabled={disabled}
- pdfBounds={pdfBounds}
- showAutomationInfo={false}
/>
- {/* Validation Alert */}
- {!isCropValid && (
+ {/* PDF Preview with Crop Selector - Only show when autoCrop is false */}
+ {!parameters.parameters.autoCrop && (
+
+
+
+ {t("crop.preview.title", "Crop Area Selection")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* Manual Coordinate Input - Only show when autoCrop is false */}
+ {!parameters.parameters.autoCrop && (
+
+ )}
+
+ {/* Validation Alert - Only show when autoCrop is false */}
+ {!parameters.parameters.autoCrop && !isCropValid && (
{t("crop.error.invalidArea", "Crop area extends beyond PDF boundaries")}
diff --git a/frontend/src/core/hooks/tools/crop/useCropOperation.ts b/frontend/src/core/hooks/tools/crop/useCropOperation.ts
index 85d1cc30d..6cc9aa469 100644
--- a/frontend/src/core/hooks/tools/crop/useCropOperation.ts
+++ b/frontend/src/core/hooks/tools/crop/useCropOperation.ts
@@ -7,13 +7,17 @@ import { CropParameters, defaultParameters } from '@app/hooks/tools/crop/useCrop
export const buildCropFormData = (parameters: CropParameters, file: File): FormData => {
const formData = new FormData();
formData.append("fileInput", file);
- const cropArea = parameters.cropArea;
- // Backend expects precise float values for PDF coordinates
- formData.append("x", cropArea.x.toString());
- formData.append("y", cropArea.y.toString());
- formData.append("width", cropArea.width.toString());
- formData.append("height", cropArea.height.toString());
+ if (!parameters.autoCrop) {
+ const cropArea = parameters.cropArea;
+
+ formData.append("x", cropArea.x.toString());
+ formData.append("y", cropArea.y.toString());
+ formData.append("width", cropArea.width.toString());
+ formData.append("height", cropArea.height.toString());
+ }
+
+ formData.append("autoCrop", parameters.autoCrop.toString());
return formData;
};
diff --git a/frontend/src/core/hooks/tools/crop/useCropParameters.ts b/frontend/src/core/hooks/tools/crop/useCropParameters.ts
index 443d79a1c..16314160e 100644
--- a/frontend/src/core/hooks/tools/crop/useCropParameters.ts
+++ b/frontend/src/core/hooks/tools/crop/useCropParameters.ts
@@ -6,10 +6,12 @@ import { DEFAULT_CROP_AREA } from '@app/constants/cropConstants';
export interface CropParameters extends BaseParameters {
cropArea: Rectangle;
+ autoCrop: boolean;
}
export const defaultParameters: CropParameters = {
cropArea: DEFAULT_CROP_AREA,
+ autoCrop: false,
};
export type CropParametersHook = BaseParametersHook & {