diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index e363636a2..7d01af4f5 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -44,6 +44,7 @@ "editYourNewFiles": "Edit your new file(s)", "close": "Close", "fileSelected": "Selected: {{filename}}", + "chooseFile": "Choose File", "filesSelected": "{{count}} files selected", "files": { "title": "Files", @@ -814,26 +815,180 @@ "submit": "Add Attachments" }, "watermark": { - "tags": "Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo", "title": "Add Watermark", - "header": "Add Watermark", - "customColor": "Custom Text Colour", - "selectText": { - "1": "Select PDF to add watermark to:", - "2": "Watermark Text:", - "3": "Font Size:", - "4": "Rotation (0-360):", - "5": "Width Spacer (Space between each watermark horizontally):", - "6": "Height Spacer (Space between each watermark vertically):", - "7": "Opacity (0% - 100%):", - "8": "Watermark Type:", - "9": "Watermark Image:", - "10": "Convert PDF to PDF-Image" - }, + "desc": "Add text or image watermarks to PDF files", + "completed": "Watermark added", "submit": "Add Watermark", - "type": { - "1": "Text", - "2": "Image" + "filenamePrefix": "watermarked", + "error": { + "failed": "An error occurred while adding watermark to the PDF." + }, + "watermarkType": { + "text": "Text", + "image": "Image" + }, + "settings": { + "type": "Watermark Type", + "text": { + "label": "Watermark Text", + "placeholder": "Enter watermark text" + }, + "image": { + "label": "Watermark Image", + "choose": "Choose Image", + "selected": "Selected: {{filename}}" + }, + "fontSize": "Font Size", + "size": "Size", + "alphabet": "Font/Language", + "color": "Watermark Colour", + "rotation": "Rotation (degrees)", + "opacity": "Opacity (%)", + "spacing": { + "horizontal": "Horizontal Spacing", + "vertical": "Vertical Spacing" + }, + "convertToImage": "Flatten PDF pages to images" + }, + "alphabet": { + "roman": "Roman/Latin", + "arabic": "Arabic", + "japanese": "Japanese", + "korean": "Korean", + "chinese": "Chinese", + "thai": "Thai" + }, + "steps": { + "type": "Watermark Type", + "wording": "Wording", + "textStyle": "Style", + "formatting": "Formatting", + "file": "Watermark File" + }, + "results": { + "title": "Watermark Results" + }, + "tooltip": { + "language": { + "title": "Language Support", + "text": "Choose the appropriate language setting to ensure proper font rendering for your text." + }, + "appearance": { + "title": "Appearance Settings", + "text": "Control how your watermark looks and blends with the document.", + "bullet1": "Rotation: -360° to 360° for angled watermarks", + "bullet2": "Opacity: 0-100% for transparency control", + "bullet3": "Lower opacity creates subtle watermarks" + }, + "spacing": { + "title": "Spacing Control", + "text": "Adjust the spacing between repeated watermarks across the page.", + "bullet1": "Width spacing: Horizontal distance between watermarks", + "bullet2": "Height spacing: Vertical distance between watermarks", + "bullet3": "Higher values create more spread out patterns" + }, + "type": { + "header": { + "title": "Watermark Type Selection" + }, + "description": { + "title": "Choose Your Watermark", + "text": "Select between text or image watermarks based on your needs." + }, + "text": { + "title": "Text Watermarks", + "text": "Perfect for adding copyright notices, company names, or confidentiality labels. Supports multiple languages and custom colours.", + "bullet1": "Customisable fonts and languages", + "bullet2": "Adjustable colours and transparency", + "bullet3": "Ideal for legal or branding text" + }, + "image": { + "title": "Image Watermarks", + "text": "Use logos, stamps, or any image as a watermark. Great for branding and visual identification.", + "bullet1": "Upload any image format", + "bullet2": "Maintains image quality", + "bullet3": "Perfect for logos and stamps" + } + }, + "wording": { + "header": { + "title": "Text Content" + }, + "text": { + "title": "Watermark Text", + "text": "Enter the text that will appear as your watermark across the document.", + "bullet1": "Keep it concise for better readability", + "bullet2": "Common examples: 'CONFIDENTIAL', 'DRAFT', company name", + "bullet3": "Emoji characters are not supported and will be filtered out" + } + }, + "textStyle": { + "header": { + "title": "Text Style" + }, + "color": { + "title": "Colour Selection", + "text": "Choose a colour that provides good contrast with your document content.", + "bullet1": "Light grey (#d3d3d3) for subtle watermarks", + "bullet2": "Black or dark colours for high contrast", + "bullet3": "Custom colours for branding purposes" + }, + "language": { + "title": "Language Support", + "text": "Choose the appropriate language setting to ensure proper font rendering." + } + }, + "file": { + "header": { + "title": "Image Upload" + }, + "upload": { + "title": "Image Selection", + "text": "Upload an image file to use as your watermark.", + "bullet1": "Supports common formats: PNG, JPG, GIF, BMP", + "bullet2": "PNG with transparency works best", + "bullet3": "Higher resolution images maintain quality better" + }, + "recommendations": { + "title": "Best Practices", + "text": "Tips for optimal image watermark results.", + "bullet1": "Use logos or stamps with transparent backgrounds", + "bullet2": "Simple designs work better than complex images", + "bullet3": "Consider the final document size when choosing resolution" + } + }, + "formatting": { + "header": { + "title": "Formatting & Layout" + }, + "size": { + "title": "Size Control", + "text": "Adjust the size of your watermark (text or image).", + "bullet1": "Larger sizes create more prominent watermarks" + }, + "appearance": { + "title": "Appearance Settings", + "text": "Control how your watermark looks and blends with the document.", + "bullet1": "Rotation: -360° to 360° for angled watermarks", + "bullet2": "Opacity: 0-100% for transparency control", + "bullet3": "Lower opacity creates subtle watermarks" + }, + "spacing": { + "title": "Spacing Control", + "text": "Adjust the spacing between repeated watermarks across the page.", + "bullet1": "Horizontal spacing: Distance between watermarks left to right", + "bullet2": "Vertical spacing: Distance between watermarks top to bottom", + "bullet3": "Higher values create more spread out patterns" + }, + "security": { + "title": "Security Option", + "text": "Convert the final PDF to an image-based format for enhanced security.", + "bullet1": "Prevents text selection and copying", + "bullet2": "Makes watermarks harder to remove", + "bullet3": "Results in larger file sizes", + "bullet4": "Best for sensitive or copyrighted content" + } + } } }, "permissions": { diff --git a/frontend/public/locales/en-US/translation.json b/frontend/public/locales/en-US/translation.json index 967d7746d..af7188944 100644 --- a/frontend/public/locales/en-US/translation.json +++ b/frontend/public/locales/en-US/translation.json @@ -43,6 +43,7 @@ "download": "Download", "editYourNewFiles": "Edit your new file(s)", "close": "Close", + "chooseFile": "Choose File", "fileSelected": "Selected: {{filename}}", "filesSelected": "{{count}} files selected", "files": { @@ -717,29 +718,6 @@ "upload": "Add image", "submit": "Add image" }, - "watermark": { - "tags": "Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo", - "title": "Add Watermark", - "header": "Add Watermark", - "customColor": "Custom Text Color", - "selectText": { - "1": "Select PDF to add watermark to:", - "2": "Watermark Text:", - "3": "Font Size:", - "4": "Rotation (0-360):", - "5": "Width Spacer (Space between each watermark horizontally):", - "6": "Height Spacer (Space between each watermark vertically):", - "7": "Opacity (0% - 100%):", - "8": "Watermark Type:", - "9": "Watermark Image:", - "10": "Convert PDF to PDF-Image" - }, - "submit": "Add Watermark", - "type": { - "1": "Text", - "2": "Image" - } - }, "permissions": { "tags": "read,write,edit,print", "title": "Change Permissions", @@ -1742,6 +1720,245 @@ } } }, + "watermark": { + "tags": "Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo", + "title": "Add Watermark", + "desc": "Add text or image watermarks to PDF files", + "header": "Add Watermark", + "completed": "Watermark added", + "submit": "Add Watermark", + "filenamePrefix": "watermarked", + "error": { + "failed": "An error occurred while adding watermark to the PDF." + }, + "watermarkType": { + "text": "Text", + "image": "Image" + }, + "settings": { + "type": "Watermark Type", + "text": { + "label": "Watermark Text", + "placeholder": "Enter watermark text" + }, + "image": { + "label": "Watermark Image", + "choose": "Choose Image", + "selected": "Selected: {{filename}}" + }, + "fontSize": "Font Size", + "alphabet": "Font/Language", + "color": "Watermark Color", + "rotation": "Rotation (degrees)", + "opacity": "Opacity (%)", + "spacing": { + "horizontal": "Horizontal Spacing", + "vertical": "Vertical Spacing" + }, + "convertToImage": "Flatten PDF pages to images" + }, + "alphabet": { + "roman": "Roman/Latin", + "arabic": "Arabic", + "japanese": "Japanese", + "korean": "Korean", + "chinese": "Chinese", + "thai": "Thai" + }, + "steps": { + "type": "Watermark Type", + "wording": "Wording", + "textStyle": "Style", + "file": "Watermark File", + "formatting": "Formatting" + }, + "results": { + "title": "Watermark Results" + }, + "tooltip": { + "language": { + "title": "Language Support", + "text": "Choose the appropriate language setting to ensure proper font rendering for your text." + }, + "appearance": { + "title": "Appearance Settings", + "text": "Control how your watermark looks and blends with the document.", + "bullet1": "Rotation: -360° to 360° for angled watermarks", + "bullet2": "Opacity: 0-100% for transparency control", + "bullet3": "Lower opacity creates subtle watermarks" + }, + "spacing": { + "title": "Spacing Control", + "text": "Adjust the spacing between repeated watermarks across the page.", + "bullet1": "Width spacing: Horizontal distance between watermarks", + "bullet2": "Height spacing: Vertical distance between watermarks", + "bullet3": "Higher values create more spread out patterns" + }, + "type": { + "header": { + "title": "Watermark Type Selection" + }, + "description": { + "title": "Choose Your Watermark", + "text": "Select between text or image watermarks based on your needs." + }, + "text": { + "title": "Text Watermarks", + "text": "Perfect for adding copyright notices, company names, or confidentiality labels. Supports multiple languages and custom colors.", + "bullet1": "Customizable fonts and languages", + "bullet2": "Adjustable colors and transparency", + "bullet3": "Ideal for legal or branding text" + }, + "image": { + "title": "Image Watermarks", + "text": "Use logos, stamps, or any image as a watermark. Great for branding and visual identification.", + "bullet1": "Upload any image format", + "bullet2": "Maintains image quality", + "bullet3": "Perfect for logos and stamps" + } + }, + "content": { + "header": { + "title": "Content Configuration" + }, + "text": { + "title": "Text Settings", + "text": "Configure your text watermark appearance and language support.", + "bullet1": "Enter your watermark text", + "bullet2": "Adjust font size (8-72pt)", + "bullet3": "Select language/script support", + "bullet4": "Choose custom colors" + }, + "language": { + "title": "Language Support", + "text": "Choose the appropriate language setting to ensure proper font rendering for your text.", + "bullet1": "Roman/Latin for Western languages", + "bullet2": "Arabic for Arabic script", + "bullet3": "Japanese, Korean, Chinese for Asian languages", + "bullet4": "Thai for Thai script" + } + }, + "style": { + "header": { + "title": "Style & Positioning" + }, + "appearance": { + "title": "Appearance Settings", + "text": "Control how your watermark looks and blends with the document.", + "bullet1": "Rotation: -360° to 360° for angled watermarks", + "bullet2": "Opacity: 0-100% for transparency control", + "bullet3": "Lower opacity creates subtle watermarks" + }, + "spacing": { + "title": "Spacing Control", + "text": "Adjust the spacing between repeated watermarks across the page.", + "bullet1": "Width spacing: Horizontal distance between watermarks", + "bullet2": "Height spacing: Vertical distance between watermarks", + "bullet3": "Higher values create more spread out patterns" + } + }, + "wording": { + "header": { + "title": "Text Content" + }, + "text": { + "title": "Watermark Text", + "text": "Enter the text that will appear as your watermark across the document.", + "bullet1": "Keep it concise for better readability", + "bullet2": "Common examples: 'CONFIDENTIAL', 'DRAFT', company name", + "bullet3": "Emoji characters are not supported and will be filtered out" + } + }, + "textStyle": { + "header": { + "title": "Text Style" + }, + "language": { + "title": "Language Support", + "text": "Choose the appropriate language setting to ensure proper font rendering.", + "bullet1": "Roman/Latin for Western languages", + "bullet2": "Arabic for Arabic script", + "bullet3": "Japanese, Korean, Chinese for Asian languages", + "bullet4": "Thai for Thai script" + }, + "color": { + "title": "Color Selection", + "text": "Choose a color that provides good contrast with your document content.", + "bullet1": "Light gray (#d3d3d3) for subtle watermarks", + "bullet2": "Black or dark colors for high contrast", + "bullet3": "Custom colors for branding purposes" + } + }, + "file": { + "header": { + "title": "Image Upload" + }, + "upload": { + "title": "Image Selection", + "text": "Upload an image file to use as your watermark.", + "bullet1": "Supports common formats: PNG, JPG, GIF, BMP", + "bullet2": "PNG with transparency works best", + "bullet3": "Higher resolution images maintain quality better" + }, + "recommendations": { + "title": "Best Practices", + "text": "Tips for optimal image watermark results.", + "bullet1": "Use logos or stamps with transparent backgrounds", + "bullet2": "Simple designs work better than complex images", + "bullet3": "Consider the final document size when choosing resolution" + } + }, + "formatting": { + "header": { + "title": "Formatting & Layout" + }, + "size": { + "title": "Size Control", + "text": "Adjust the size of your watermark (text or image).", + "bullet1": "Larger sizes create more prominent watermarks" + }, + "appearance": { + "title": "Appearance Settings", + "text": "Control how your watermark looks and blends with the document.", + "bullet1": "Rotation: -360° to 360° for angled watermarks", + "bullet2": "Opacity: 0-100% for transparency control", + "bullet3": "Lower opacity creates subtle watermarks" + }, + "spacing": { + "title": "Spacing Control", + "text": "Adjust the spacing between repeated watermarks across the page.", + "bullet1": "Horizontal spacing: Distance between watermarks left to right", + "bullet2": "Vertical spacing: Distance between watermarks top to bottom", + "bullet3": "Higher values create more spread out patterns" + }, + "security": { + "title": "Security Option", + "text": "Flatten PDF pages to images for enhanced security.", + "bullet1": "Prevents text selection and copying", + "bullet2": "Makes watermarks harder to remove", + "bullet3": "Results in larger file sizes", + "bullet4": "Best for sensitive or copyrighted content" + } + }, + "advanced": { + "header": { + "title": "Advanced Options" + }, + "conversion": { + "title": "PDF to Image Conversion", + "text": "Convert the final PDF to an image-based format for enhanced security.", + "bullet1": "Prevents text selection and copying", + "bullet2": "Makes watermarks harder to remove", + "bullet3": "Results in larger file sizes", + "bullet4": "Best for sensitive or copyrighted content" + }, + "security": { + "title": "Security Considerations", + "text": "Image-based PDFs provide additional protection against unauthorized editing and content extraction." + } + } + } + }, "removePassword": { "title": "Remove Password", "desc": "Remove password protection from your PDF document.", diff --git a/frontend/src/components/shared/FileUploadButton.tsx b/frontend/src/components/shared/FileUploadButton.tsx new file mode 100644 index 000000000..27f58400f --- /dev/null +++ b/frontend/src/components/shared/FileUploadButton.tsx @@ -0,0 +1,45 @@ +import React, { useRef } from "react"; +import { FileButton, Button } from "@mantine/core"; +import { useTranslation } from "react-i18next"; + +interface FileUploadButtonProps { + file?: File; + onChange: (file: File | null) => void; + accept?: string; + disabled?: boolean; + placeholder?: string; + variant?: "outline" | "filled" | "light" | "default" | "subtle" | "gradient"; + fullWidth?: boolean; +} + +const FileUploadButton = ({ + file, + onChange, + accept = "*/*", + disabled = false, + placeholder, + variant = "outline", + fullWidth = true +}: FileUploadButtonProps) => { + const { t } = useTranslation(); + const resetRef = useRef<() => void>(null); + + const defaultPlaceholder = t('chooseFile', 'Choose File'); + + return ( + + {(props) => ( + + )} + + ); +}; + +export default FileUploadButton; diff --git a/frontend/src/components/shared/Tooltip.tsx b/frontend/src/components/shared/Tooltip.tsx index 0acc78057..c415eddf5 100644 --- a/frontend/src/components/shared/Tooltip.tsx +++ b/frontend/src/components/shared/Tooltip.tsx @@ -2,7 +2,8 @@ import React, { useState, useRef, useEffect } from 'react'; import { createPortal } from 'react-dom'; import { isClickOutside, addEventListenerWithCleanup } from '../../utils/genericUtils'; import { useTooltipPosition } from '../../hooks/useTooltipPosition'; -import { TooltipContent, TooltipTip } from './tooltip/TooltipContent'; +import { TooltipTip } from '../../types/tips'; +import { TooltipContent } from './tooltip/TooltipContent'; import { useSidebarContext } from '../../contexts/SidebarContext'; import styles from './tooltip/Tooltip.module.css' diff --git a/frontend/src/components/shared/tooltip/TooltipContent.tsx b/frontend/src/components/shared/tooltip/TooltipContent.tsx index e3515e0e6..0e347cd1c 100644 --- a/frontend/src/components/shared/tooltip/TooltipContent.tsx +++ b/frontend/src/components/shared/tooltip/TooltipContent.tsx @@ -1,12 +1,6 @@ import React from 'react'; import styles from './Tooltip.module.css'; - -export interface TooltipTip { - title?: string; - description?: string; - bullets?: string[]; - body?: React.ReactNode; -} +import { TooltipTip } from '../../../types/tips'; interface TooltipContentProps { content?: React.ReactNode; diff --git a/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx b/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx new file mode 100644 index 000000000..9a267f638 --- /dev/null +++ b/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx @@ -0,0 +1,83 @@ +import React from "react"; +import { Stack, Checkbox, Group } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { AddWatermarkParameters } from "../../../hooks/tools/addWatermark/useAddWatermarkParameters"; +import NumberInputWithUnit from "../shared/NumberInputWithUnit"; + +interface WatermarkFormattingProps { + parameters: AddWatermarkParameters; + onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + disabled?: boolean; +} + +const WatermarkFormatting = ({ parameters, onParameterChange, disabled = false }: WatermarkFormattingProps) => { + const { t } = useTranslation(); + + return ( + + {/* Size - single row */} + onParameterChange('fontSize', typeof value === 'number' ? value : 12)} + unit={parameters.watermarkType === 'text' ? 'pt' : 'px'} + min={1} + disabled={disabled} + /> + + {/* Position & Appearance - 2 per row */} + + onParameterChange('rotation', typeof value === 'number' ? value : 0)} + unit="°" + min={-360} + max={360} + disabled={disabled} + /> + onParameterChange('opacity', typeof value === 'number' ? value : 50)} + unit="%" + min={0} + max={100} + disabled={disabled} + /> + + + {/* Spacing - 2 per row */} + + onParameterChange('widthSpacer', typeof value === 'number' ? value : 50)} + unit="px" + min={0} + max={200} + disabled={disabled} + /> + onParameterChange('heightSpacer', typeof value === 'number' ? value : 50)} + unit="px" + min={0} + max={200} + disabled={disabled} + /> + + + {/* Advanced Options */} + onParameterChange('convertPDFToImage', event.currentTarget.checked)} + disabled={disabled} + /> + + ); +}; + +export default WatermarkFormatting; \ No newline at end of file diff --git a/frontend/src/components/tools/addWatermark/WatermarkImageFile.tsx b/frontend/src/components/tools/addWatermark/WatermarkImageFile.tsx new file mode 100644 index 000000000..6f38ae206 --- /dev/null +++ b/frontend/src/components/tools/addWatermark/WatermarkImageFile.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { Stack } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { AddWatermarkParameters } from "../../../hooks/tools/addWatermark/useAddWatermarkParameters"; +import FileUploadButton from "../../shared/FileUploadButton"; + +interface WatermarkImageFileProps { + parameters: AddWatermarkParameters; + onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + disabled?: boolean; +} + +const WatermarkImageFile = ({ parameters, onParameterChange, disabled = false }: WatermarkImageFileProps) => { + const { t } = useTranslation(); + + return ( + + onParameterChange('watermarkImage', file)} + accept="image/*" + disabled={disabled} + placeholder={t('watermark.settings.image.choose', 'Choose Image')} + /> + + ); +}; + +export default WatermarkImageFile; diff --git a/frontend/src/components/tools/addWatermark/WatermarkStyleSettings.tsx b/frontend/src/components/tools/addWatermark/WatermarkStyleSettings.tsx new file mode 100644 index 000000000..2de9335b0 --- /dev/null +++ b/frontend/src/components/tools/addWatermark/WatermarkStyleSettings.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import { Stack, Text, NumberInput } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { AddWatermarkParameters } from "../../../hooks/tools/addWatermark/useAddWatermarkParameters"; + +interface WatermarkStyleSettingsProps { + parameters: AddWatermarkParameters; + onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + disabled?: boolean; +} + +const WatermarkStyleSettings = ({ parameters, onParameterChange, disabled = false }: WatermarkStyleSettingsProps) => { + const { t } = useTranslation(); + + return ( + + {/* Appearance Settings */} + + {t('watermark.settings.rotation', 'Rotation (degrees)')} + onParameterChange('rotation', value || 0)} + min={-360} + max={360} + disabled={disabled} + /> + + {t('watermark.settings.opacity', 'Opacity (%)')} + onParameterChange('opacity', value || 50)} + min={0} + max={100} + disabled={disabled} + /> + + + {/* Spacing Settings */} + + {t('watermark.settings.spacing.width', 'Width Spacing')} + onParameterChange('widthSpacer', value || 50)} + min={0} + max={200} + disabled={disabled} + /> + + {t('watermark.settings.spacing.height', 'Height Spacing')} + onParameterChange('heightSpacer', value || 50)} + min={0} + max={200} + disabled={disabled} + /> + + + + ); +}; + +export default WatermarkStyleSettings; \ No newline at end of file diff --git a/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx b/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx new file mode 100644 index 000000000..00fd21d09 --- /dev/null +++ b/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { Stack, Text, Select, ColorInput } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { AddWatermarkParameters } from "../../../hooks/tools/addWatermark/useAddWatermarkParameters"; +import { alphabetOptions } from "../../../constants/addWatermarkConstants"; + +interface WatermarkTextStyleProps { + parameters: AddWatermarkParameters; + onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + disabled?: boolean; +} + +const WatermarkTextStyle = ({ parameters, onParameterChange, disabled = false }: WatermarkTextStyleProps) => { + const { t } = useTranslation(); + + + return ( + + + + {t("watermark.settings.color", "Colour")} + + onParameterChange("customColor", value)} + disabled={disabled} + format="hex" + /> + + + + + {t("watermark.settings.alphabet", "Alphabet")} + +