import { useMemo, useState, useEffect } from "react"; import { Stack, Text, Box, Group, NumberInput, ActionIcon, Center, Alert } from "@mantine/core"; import { useTranslation } from "react-i18next"; import RestartAltIcon from "@mui/icons-material/RestartAlt"; import { CropParametersHook } from "../../../hooks/tools/crop/useCropParameters"; import { useSelectedFiles } from "../../../contexts/file/fileHooks"; import CropAreaSelector from "./CropAreaSelector"; import { calculatePDFBounds, PDFBounds, CropArea } from "../../../utils/cropCoordinates"; import { pdfWorkerManager } from "../../../services/pdfWorkerManager"; import DocumentThumbnail from "../../shared/filePreview/DocumentThumbnail"; interface CropSettingsProps { parameters: CropParametersHook; disabled?: boolean; } const CONTAINER_SIZE = 250; // Fit within actual pane width const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => { const { t } = useTranslation(); const { selectedFiles, selectedFileStubs } = useSelectedFiles(); // Get the first selected file for preview const selectedStub = useMemo(() => { return selectedFileStubs.length > 0 ? selectedFileStubs[0] : null; }, [selectedFileStubs]); // Get the first selected file for PDF processing const selectedFile = useMemo(() => { return selectedFiles.length > 0 ? selectedFiles[0] : null; }, [selectedFiles]); // Get thumbnail for the selected file const [thumbnail, setThumbnail] = useState(null); const [pdfBounds, setPdfBounds] = useState(null); useEffect(() => { const loadPDFDimensions = async () => { if (!selectedStub || !selectedFile) { setPdfBounds(null); setThumbnail(null); return; } setThumbnail(selectedStub.thumbnailUrl || null); try { // Get PDF dimensions from the actual file const arrayBuffer = await selectedFile.arrayBuffer(); // Load PDF to get actual dimensions const pdf = await pdfWorkerManager.createDocument(arrayBuffer, { disableAutoFetch: true, disableStream: true, stopAtErrors: false }); const firstPage = await pdf.getPage(1); const viewport = firstPage.getViewport({ scale: 1 }); const pdfWidth = viewport.width; const pdfHeight = viewport.height; const bounds = calculatePDFBounds(pdfWidth, pdfHeight, CONTAINER_SIZE, CONTAINER_SIZE); setPdfBounds(bounds); // Initialize crop area to full PDF if parameters are still default const isDefault = parameters.parameters.width === 595 && parameters.parameters.height === 842 && parameters.parameters.x === 0 && parameters.parameters.y === 0; if (isDefault) { parameters.resetToFullPDF(bounds); } // Cleanup PDF pdfWorkerManager.destroyDocument(pdf); } catch (error) { console.error('Failed to load PDF dimensions:', error); // Fallback to A4 dimensions if PDF loading fails const bounds = calculatePDFBounds(595, 842, CONTAINER_SIZE, CONTAINER_SIZE); setPdfBounds(bounds); if (parameters.parameters.width === 595 && parameters.parameters.height === 842) { parameters.resetToFullPDF(bounds); } } }; loadPDFDimensions(); }, [selectedStub, selectedFile, parameters]); // Current crop area const cropArea = parameters.getCropArea(); // Handle crop area changes from the selector const handleCropAreaChange = (newCropArea: CropArea) => { if (pdfBounds) { parameters.setCropArea(newCropArea, pdfBounds); } }; // Handle manual coordinate input changes const handleCoordinateChange = (field: keyof CropArea, value: number | string) => { const numValue = typeof value === 'string' ? parseFloat(value) : value; if (isNaN(numValue)) return; const newCropArea = { ...cropArea, [field]: numValue }; if (pdfBounds) { parameters.setCropArea(newCropArea, pdfBounds); } }; // Reset to full PDF const handleReset = () => { if (pdfBounds) { parameters.resetToFullPDF(pdfBounds); } }; if (!selectedStub || !pdfBounds) { return (
{t("crop.noFileSelected", "Select a PDF file to begin cropping")}
); } const isCropValid = parameters.isCropAreaValid(pdfBounds); const isFullCrop = parameters.isFullPDFCrop(pdfBounds); return ( {/* PDF Preview with Crop Selector */} {t("crop.preview.title", "Crop Area Selection")}
{/* Manual Coordinate Input */} {t("crop.coordinates.title", "Precise Coordinates (PDF Points)")} handleCoordinateChange('x', value)} disabled={disabled} min={0} max={pdfBounds.actualWidth} step={0.1} decimalScale={1} size="xs" /> handleCoordinateChange('y', value)} disabled={disabled} min={0} max={pdfBounds.actualHeight} step={0.1} decimalScale={1} size="xs" /> handleCoordinateChange('width', value)} disabled={disabled} min={0.1} max={pdfBounds.actualWidth} step={0.1} decimalScale={1} size="xs" /> handleCoordinateChange('height', value)} disabled={disabled} min={0.1} max={pdfBounds.actualHeight} step={0.1} decimalScale={1} size="xs" /> {/* Validation Alert */} {!isCropValid && ( {t("crop.error.invalidArea", "Crop area extends beyond PDF boundaries")} )}
); }; export default CropSettings;