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`. image --- ## 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 & {