mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 14:19:24 +00:00
Working
This commit is contained in:
parent
9f8a9e45b1
commit
0bd050396c
@ -1766,11 +1766,10 @@
|
||||
"rotation": "Rotation (degrees)",
|
||||
"opacity": "Opacity (%)",
|
||||
"spacing": {
|
||||
"width": "Width Spacing",
|
||||
"height": "Height Spacing"
|
||||
"horizontal": "Horizontal Spacing",
|
||||
"vertical": "Vertical Spacing"
|
||||
},
|
||||
"convertToImage": "Convert result to image-based PDF",
|
||||
"convertToImageDesc": "Creates a PDF with images instead of text (more secure but larger file size)"
|
||||
"convertToImage": "Flatten PDF pages to images"
|
||||
},
|
||||
"alphabet": {
|
||||
"roman": "Roman/Latin",
|
||||
@ -1782,10 +1781,10 @@
|
||||
},
|
||||
"steps": {
|
||||
"type": "Watermark Type",
|
||||
"textContent": "Text Content",
|
||||
"imageContent": "Image Content",
|
||||
"style": "Style & Position",
|
||||
"advanced": "Advanced Options"
|
||||
"wording": "Wording",
|
||||
"textStyle": "Style",
|
||||
"file": "Watermark File",
|
||||
"formatting": "Formatting"
|
||||
},
|
||||
"results": {
|
||||
"title": "Watermark Results"
|
||||
@ -1854,6 +1853,91 @@
|
||||
"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": "Supports all Unicode characters"
|
||||
},
|
||||
"fontSize": {
|
||||
"title": "Font Size",
|
||||
"text": "Control the size of your watermark text (8-72 points).",
|
||||
"bullet1": "Smaller sizes (8-14pt) for subtle watermarks",
|
||||
"bullet2": "Medium sizes (16-24pt) for standard visibility",
|
||||
"bullet3": "Large sizes (30-72pt) for prominent watermarks"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"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"
|
||||
|
@ -1,93 +0,0 @@
|
||||
import React, { useRef } from "react";
|
||||
import { Stack, Text, TextInput, FileButton, Button, NumberInput, Select, ColorInput } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AddWatermarkParameters } from "./types";
|
||||
|
||||
interface WatermarkContentSettingsProps {
|
||||
parameters: AddWatermarkParameters;
|
||||
onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const WatermarkContentSettings = ({ parameters, onParameterChange, disabled = false }: WatermarkContentSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const resetRef = useRef<() => void>(null);
|
||||
|
||||
const alphabetOptions = [
|
||||
{ value: 'roman', label: t('watermark.alphabet.roman', 'Roman/Latin') },
|
||||
{ value: 'arabic', label: t('watermark.alphabet.arabic', 'Arabic') },
|
||||
{ value: 'japanese', label: t('watermark.alphabet.japanese', 'Japanese') },
|
||||
{ value: 'korean', label: t('watermark.alphabet.korean', 'Korean') },
|
||||
{ value: 'chinese', label: t('watermark.alphabet.chinese', 'Chinese') },
|
||||
{ value: 'thai', label: t('watermark.alphabet.thai', 'Thai') }
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{/* Text Watermark Settings */}
|
||||
{parameters.watermarkType === 'text' && (
|
||||
<Stack gap="sm">
|
||||
<Text size="sm" fw={500}>{t('watermark.settings.text.label', 'Watermark Text')}</Text>
|
||||
<TextInput
|
||||
placeholder={t('watermark.settings.text.placeholder', 'Enter watermark text')}
|
||||
value={parameters.watermarkText}
|
||||
onChange={(e) => onParameterChange('watermarkText', e.target.value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<Text size="sm" fw={500}>{t('watermark.settings.fontSize', 'Font Size')}</Text>
|
||||
<NumberInput
|
||||
value={parameters.fontSize}
|
||||
onChange={(value) => onParameterChange('fontSize', value || 12)}
|
||||
min={8}
|
||||
max={72}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<Text size="sm" fw={500}>{t('watermark.settings.alphabet', 'Font/Language')}</Text>
|
||||
<Select
|
||||
value={parameters.alphabet}
|
||||
onChange={(value) => value && onParameterChange('alphabet', value)}
|
||||
data={alphabetOptions}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<Text size="sm" fw={500}>{t('watermark.settings.color', 'Watermark Color')}</Text>
|
||||
<ColorInput
|
||||
value={parameters.customColor}
|
||||
onChange={(value) => onParameterChange('customColor', value)}
|
||||
disabled={disabled}
|
||||
format="hex"
|
||||
swatches={['#d3d3d3', '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{/* Image Watermark Settings */}
|
||||
{parameters.watermarkType === 'image' && (
|
||||
<Stack gap="sm">
|
||||
<Text size="sm" fw={500}>{t('watermark.settings.image.label', 'Watermark Image')}</Text>
|
||||
<FileButton
|
||||
resetRef={resetRef}
|
||||
onChange={(file) => onParameterChange('watermarkImage', file)}
|
||||
accept="image/*"
|
||||
disabled={disabled}
|
||||
>
|
||||
{(props) => (
|
||||
<Button {...props} variant="outline" fullWidth>
|
||||
{parameters.watermarkImage ? parameters.watermarkImage.name : t('watermark.settings.image.choose', 'Choose Image')}
|
||||
</Button>
|
||||
)}
|
||||
</FileButton>
|
||||
{parameters.watermarkImage && (
|
||||
<Text size="xs" c="dimmed">
|
||||
{t('watermark.settings.image.selected', 'Selected: {{filename}}', { filename: parameters.watermarkImage.name })}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default WatermarkContentSettings;
|
34
frontend/src/components/tools/addWatermark/WatermarkFile.tsx
Normal file
34
frontend/src/components/tools/addWatermark/WatermarkFile.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { useRef } from "react";
|
||||
import { Stack, Text, FileButton, Button } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AddWatermarkParameters } from "./types";
|
||||
|
||||
interface WatermarkFileProps {
|
||||
parameters: AddWatermarkParameters;
|
||||
onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const WatermarkFile = ({ parameters, onParameterChange, disabled = false }: WatermarkFileProps) => {
|
||||
const { t } = useTranslation();
|
||||
const resetRef = useRef<() => void>(null);
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
<FileButton
|
||||
resetRef={resetRef}
|
||||
onChange={(file) => onParameterChange('watermarkImage', file)}
|
||||
accept="image/*"
|
||||
disabled={disabled}
|
||||
>
|
||||
{(props) => (
|
||||
<Button {...props} variant="outline" fullWidth>
|
||||
{parameters.watermarkImage ? parameters.watermarkImage.name : t('watermark.settings.image.choose', 'Choose Image')}
|
||||
</Button>
|
||||
)}
|
||||
</FileButton>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default WatermarkFile;
|
@ -0,0 +1,73 @@
|
||||
import React from "react";
|
||||
import { Stack, Checkbox, Group } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AddWatermarkParameters } from "./types";
|
||||
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 (
|
||||
<Stack gap="md">
|
||||
{/* Position & Appearance - 2 per row */}
|
||||
<Group grow align="flex-start">
|
||||
<NumberInputWithUnit
|
||||
label={t('watermark.settings.rotation', 'Rotation')}
|
||||
value={parameters.rotation}
|
||||
onChange={(value) => onParameterChange('rotation', typeof value === 'number' ? value : 0)}
|
||||
unit="°"
|
||||
min={-360}
|
||||
max={360}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<NumberInputWithUnit
|
||||
label={t('watermark.settings.opacity', 'Opacity')}
|
||||
value={parameters.opacity}
|
||||
onChange={(value) => onParameterChange('opacity', typeof value === 'number' ? value : 50)}
|
||||
unit="%"
|
||||
min={0}
|
||||
max={100}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
{/* Spacing - 2 per row */}
|
||||
<Group grow align="flex-start">
|
||||
<NumberInputWithUnit
|
||||
label={t('watermark.settings.spacing.horizontal', 'Horizontal Spacing')}
|
||||
value={parameters.widthSpacer}
|
||||
onChange={(value) => onParameterChange('widthSpacer', typeof value === 'number' ? value : 50)}
|
||||
unit="px"
|
||||
min={0}
|
||||
max={200}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<NumberInputWithUnit
|
||||
label={t('watermark.settings.spacing.vertical', 'Vertical Spacing')}
|
||||
value={parameters.heightSpacer}
|
||||
onChange={(value) => onParameterChange('heightSpacer', typeof value === 'number' ? value : 50)}
|
||||
unit="px"
|
||||
min={0}
|
||||
max={200}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
{/* Advanced Options */}
|
||||
<Checkbox
|
||||
label={t('watermark.settings.convertToImage', 'Flatten PDF pages to images')}
|
||||
checked={parameters.convertPDFToImage}
|
||||
onChange={(event) => onParameterChange('convertPDFToImage', event.currentTarget.checked)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default WatermarkFormatting;
|
@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
import { Stack, Text, Select, ColorInput, NumberInput, Group } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AddWatermarkParameters } from "./types";
|
||||
|
||||
interface WatermarkTextStyleProps {
|
||||
parameters: AddWatermarkParameters;
|
||||
onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const WatermarkTextStyle = ({ parameters, onParameterChange, disabled = false }: WatermarkTextStyleProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const alphabetOptions = [
|
||||
{ value: "roman", label: "Roman" },
|
||||
{ value: "arabic", label: "العربية" },
|
||||
{ value: "japanese", label: "日本語" },
|
||||
{ value: "korean", label: "한국어" },
|
||||
{ value: "chinese", label: "简体中文" },
|
||||
{ value: "thai", label: "ไทย" },
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
<Group align="flex-start">
|
||||
<Stack gap="xs" style={{ flex: 1 }}>
|
||||
<Text size="xs" fw={500}>
|
||||
{t("watermark.settings.color", "Colour")}
|
||||
</Text>
|
||||
<ColorInput
|
||||
value={parameters.customColor}
|
||||
onChange={(value) => onParameterChange("customColor", value)}
|
||||
disabled={disabled}
|
||||
format="hex"
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Stack gap="xs" style={{ flex: 0.5 }}>
|
||||
<Text size="xs" fw={500}>
|
||||
{t("watermark.settings.fontSize", "Size")}
|
||||
</Text>
|
||||
<NumberInput
|
||||
value={parameters.fontSize}
|
||||
onChange={(value) => onParameterChange("fontSize", value || 12)}
|
||||
min={8}
|
||||
max={72}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Stack>
|
||||
</Group>
|
||||
<Text size="xs" fw={500}>
|
||||
{t("watermark.settings.alphabet", "Alphabet")}
|
||||
</Text>
|
||||
<Select
|
||||
value={parameters.alphabet}
|
||||
onChange={(value) => value && onParameterChange("alphabet", value)}
|
||||
data={alphabetOptions}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default WatermarkTextStyle;
|
@ -13,7 +13,6 @@ const WatermarkTypeSettings = ({ watermarkType, onWatermarkTypeChange, disabled
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
<Text size="sm" fw={500}>{t('watermark.settings.type', 'Watermark Type')}</Text>
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<Button
|
||||
variant={watermarkType === 'text' ? 'filled' : 'outline'}
|
||||
|
@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import { Stack, Text, TextInput } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AddWatermarkParameters } from "./types";
|
||||
|
||||
interface WatermarkWordingProps {
|
||||
parameters: AddWatermarkParameters;
|
||||
onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const WatermarkWording = ({ parameters, onParameterChange, disabled = false }: WatermarkWordingProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
<TextInput
|
||||
placeholder={t('watermark.settings.text.placeholder', 'Enter watermark text')}
|
||||
value={parameters.watermarkText}
|
||||
onChange={(e) => onParameterChange('watermarkText', e.target.value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default WatermarkWording;
|
50
frontend/src/components/tools/shared/NumberInputWithUnit.tsx
Normal file
50
frontend/src/components/tools/shared/NumberInputWithUnit.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import { Stack, Text, NumberInput } from "@mantine/core";
|
||||
|
||||
interface NumberInputWithUnitProps {
|
||||
label: string;
|
||||
value: number;
|
||||
onChange: (value: number | string) => void;
|
||||
unit: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const NumberInputWithUnit = ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
unit,
|
||||
min,
|
||||
max,
|
||||
disabled = false
|
||||
}: NumberInputWithUnitProps) => {
|
||||
return (
|
||||
<Stack gap="xs" style={{ flex: 1 }}>
|
||||
<Text size="xs" fw={500} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||
{label}
|
||||
</Text>
|
||||
<NumberInput
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
min={min}
|
||||
max={max}
|
||||
disabled={disabled}
|
||||
rightSection={
|
||||
<Text size="sm" c="dimmed" pr="sm">
|
||||
{unit}
|
||||
</Text>
|
||||
}
|
||||
rightSectionWidth={unit.length * 8 + 20} // Dynamic width based on unit length
|
||||
styles={{
|
||||
rightSection: {
|
||||
pointerEvents: 'none',
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default NumberInputWithUnit;
|
@ -97,27 +97,132 @@ export const useWatermarkStyleTips = (): TooltipContent => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useWatermarkAdvancedTips = (): TooltipContent => {
|
||||
export const useWatermarkWordingTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.advanced.header.title", "Advanced Options")
|
||||
title: t("watermark.tooltip.wording.header.title", "Text Content")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.advanced.conversion.title", "PDF to Image Conversion"),
|
||||
description: t("watermark.tooltip.advanced.conversion.text", "Convert the final PDF to an image-based format for enhanced security."),
|
||||
title: t("watermark.tooltip.wording.text.title", "Watermark Text"),
|
||||
description: t("watermark.tooltip.wording.text.text", "Enter the text that will appear as your watermark across the document."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.advanced.conversion.bullet1", "Prevents text selection and copying"),
|
||||
t("watermark.tooltip.advanced.conversion.bullet2", "Makes watermarks harder to remove"),
|
||||
t("watermark.tooltip.advanced.conversion.bullet3", "Results in larger file sizes"),
|
||||
t("watermark.tooltip.advanced.conversion.bullet4", "Best for sensitive or copyrighted content")
|
||||
t("watermark.tooltip.wording.text.bullet1", "Keep it concise for better readability"),
|
||||
t("watermark.tooltip.wording.text.bullet2", "Common examples: 'CONFIDENTIAL', 'DRAFT', company name"),
|
||||
t("watermark.tooltip.wording.text.bullet3", "Supports all Unicode characters")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.advanced.security.title", "Security Considerations"),
|
||||
description: t("watermark.tooltip.advanced.security.text", "Image-based PDFs provide additional protection against unauthorized editing and content extraction.")
|
||||
title: t("watermark.tooltip.wording.fontSize.title", "Font Size"),
|
||||
description: t("watermark.tooltip.wording.fontSize.text", "Control the size of your watermark text (8-72 points)."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.wording.fontSize.bullet1", "Smaller sizes (8-14pt) for subtle watermarks"),
|
||||
t("watermark.tooltip.wording.fontSize.bullet2", "Medium sizes (16-24pt) for standard visibility"),
|
||||
t("watermark.tooltip.wording.fontSize.bullet3", "Large sizes (30-72pt) for prominent watermarks")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
export const useWatermarkTextStyleTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.textStyle.header.title", "Text Style")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.textStyle.language.title", "Language Support"),
|
||||
description: t("watermark.tooltip.textStyle.language.text", "Choose the appropriate language setting to ensure proper font rendering."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.textStyle.language.bullet1", "Roman/Latin for Western languages"),
|
||||
t("watermark.tooltip.textStyle.language.bullet2", "Arabic for Arabic script"),
|
||||
t("watermark.tooltip.textStyle.language.bullet3", "Japanese, Korean, Chinese for Asian languages"),
|
||||
t("watermark.tooltip.textStyle.language.bullet4", "Thai for Thai script")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.textStyle.color.title", "Color Selection"),
|
||||
description: t("watermark.tooltip.textStyle.color.text", "Choose a color that provides good contrast with your document content."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.textStyle.color.bullet1", "Light gray (#d3d3d3) for subtle watermarks"),
|
||||
t("watermark.tooltip.textStyle.color.bullet2", "Black or dark colors for high contrast"),
|
||||
t("watermark.tooltip.textStyle.color.bullet3", "Custom colors for branding purposes")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
export const useWatermarkFileTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.file.header.title", "Image Upload")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.file.upload.title", "Image Selection"),
|
||||
description: t("watermark.tooltip.file.upload.text", "Upload an image file to use as your watermark."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.file.upload.bullet1", "Supports common formats: PNG, JPG, GIF, BMP"),
|
||||
t("watermark.tooltip.file.upload.bullet2", "PNG with transparency works best"),
|
||||
t("watermark.tooltip.file.upload.bullet3", "Higher resolution images maintain quality better")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.file.recommendations.title", "Best Practices"),
|
||||
description: t("watermark.tooltip.file.recommendations.text", "Tips for optimal image watermark results."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.file.recommendations.bullet1", "Use logos or stamps with transparent backgrounds"),
|
||||
t("watermark.tooltip.file.recommendations.bullet2", "Simple designs work better than complex images"),
|
||||
t("watermark.tooltip.file.recommendations.bullet3", "Consider the final document size when choosing resolution")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
export const useWatermarkFormattingTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.formatting.header.title", "Formatting & Layout")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.formatting.appearance.title", "Appearance Settings"),
|
||||
description: t("watermark.tooltip.formatting.appearance.text", "Control how your watermark looks and blends with the document."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.formatting.appearance.bullet1", "Rotation: -360° to 360° for angled watermarks"),
|
||||
t("watermark.tooltip.formatting.appearance.bullet2", "Opacity: 0-100% for transparency control"),
|
||||
t("watermark.tooltip.formatting.appearance.bullet3", "Lower opacity creates subtle watermarks")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.formatting.spacing.title", "Spacing Control"),
|
||||
description: t("watermark.tooltip.formatting.spacing.text", "Adjust the spacing between repeated watermarks across the page."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.formatting.spacing.bullet1", "Width spacing: Horizontal distance between watermarks"),
|
||||
t("watermark.tooltip.formatting.spacing.bullet2", "Height spacing: Vertical distance between watermarks"),
|
||||
t("watermark.tooltip.formatting.spacing.bullet3", "Higher values create more spread out patterns")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.formatting.security.title", "Security Option"),
|
||||
description: t("watermark.tooltip.formatting.security.text", "Convert the final PDF to an image-based format for enhanced security."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.formatting.security.bullet1", "Prevents text selection and copying"),
|
||||
t("watermark.tooltip.formatting.security.bullet2", "Makes watermarks harder to remove"),
|
||||
t("watermark.tooltip.formatting.security.bullet3", "Results in larger file sizes"),
|
||||
t("watermark.tooltip.formatting.security.bullet4", "Best for sensitive or copyrighted content")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -7,13 +7,14 @@ import { useToolFileSelection } from "../contexts/FileSelectionContext";
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
|
||||
import WatermarkTypeSettings from "../components/tools/addWatermark/WatermarkTypeSettings";
|
||||
import WatermarkContentSettings from "../components/tools/addWatermark/WatermarkContentSettings";
|
||||
import WatermarkStyleSettings from "../components/tools/addWatermark/WatermarkStyleSettings";
|
||||
import WatermarkAdvancedSettings from "../components/tools/addWatermark/WatermarkAdvancedSettings";
|
||||
import WatermarkWording from "../components/tools/addWatermark/WatermarkWording";
|
||||
import WatermarkTextStyle from "../components/tools/addWatermark/WatermarkTextStyle";
|
||||
import WatermarkFile from "../components/tools/addWatermark/WatermarkFile";
|
||||
import WatermarkFormatting from "../components/tools/addWatermark/WatermarkFormatting";
|
||||
|
||||
import { useAddWatermarkParameters } from "../hooks/tools/addWatermark/useAddWatermarkParameters";
|
||||
import { useAddWatermarkOperation } from "../hooks/tools/addWatermark/useAddWatermarkOperation";
|
||||
import { useWatermarkTypeTips, useWatermarkContentTips, useWatermarkStyleTips, useWatermarkAdvancedTips } from "../components/tooltips/useWatermarkTips";
|
||||
import { useWatermarkTypeTips, useWatermarkWordingTips, useWatermarkTextStyleTips, useWatermarkFileTips, useWatermarkFormattingTips } from "../components/tooltips/useWatermarkTips";
|
||||
import { BaseToolProps } from "../types/tool";
|
||||
|
||||
const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
@ -28,9 +29,10 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
|
||||
const watermarkParams = useAddWatermarkParameters();
|
||||
const watermarkOperation = useAddWatermarkOperation();
|
||||
const watermarkTypeTips = useWatermarkTypeTips();
|
||||
const watermarkContentTips = useWatermarkContentTips();
|
||||
const watermarkStyleTips = useWatermarkStyleTips();
|
||||
const watermarkAdvancedTips = useWatermarkAdvancedTips();
|
||||
const watermarkWordingTips = useWatermarkWordingTips();
|
||||
const watermarkTextStyleTips = useWatermarkTextStyleTips();
|
||||
const watermarkFileTips = useWatermarkFileTips();
|
||||
const watermarkFormattingTips = useWatermarkFormattingTips();
|
||||
|
||||
// Endpoint validation
|
||||
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled("add-watermark");
|
||||
@ -78,24 +80,22 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
|
||||
|
||||
// Step completion logic
|
||||
const typeStepCompleted = hasFiles && !!watermarkParams.parameters.watermarkType;
|
||||
const contentStepCompleted = typeStepCompleted && (
|
||||
(watermarkParams.parameters.watermarkType === 'text' && watermarkParams.parameters.watermarkText.trim().length > 0) ||
|
||||
(watermarkParams.parameters.watermarkType === 'image' && watermarkParams.parameters.watermarkImage !== undefined)
|
||||
);
|
||||
const textWordingCompleted = typeStepCompleted &&
|
||||
watermarkParams.parameters.watermarkType === 'text' &&
|
||||
watermarkParams.parameters.watermarkText.trim().length > 0;
|
||||
const imageFileCompleted = typeStepCompleted &&
|
||||
watermarkParams.parameters.watermarkType === 'image' &&
|
||||
watermarkParams.parameters.watermarkImage !== undefined;
|
||||
|
||||
// Step visibility logic - all steps always visible once files are selected
|
||||
const styleCollapsed = collapsedStyle || hasResults;
|
||||
const advancedCollapsed = collapsedAdvanced || hasResults;
|
||||
// Dynamic step structure based on watermark type
|
||||
const getSteps = () => {
|
||||
const steps = [];
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles,
|
||||
isCollapsed: hasFiles || hasResults,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
// Step 1: Watermark Type (always visible after files)
|
||||
if (hasFiles) {
|
||||
steps.push({
|
||||
title: t("watermark.steps.type", "Watermark Type"),
|
||||
isCollapsed: settingsCollapsed? true : collapsedType,
|
||||
isCollapsed: hasResults ? true : collapsedType,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedType(!collapsedType),
|
||||
tooltip: watermarkTypeTips,
|
||||
content: (
|
||||
@ -105,48 +105,97 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: watermarkParams.parameters.watermarkType === 'text'
|
||||
? t("watermark.steps.textContent", "Text Content")
|
||||
: t("watermark.steps.imageContent", "Image Content"),
|
||||
isCollapsed: settingsCollapsed? true : contentStepCompleted,
|
||||
tooltip: watermarkContentTips,
|
||||
});
|
||||
}
|
||||
|
||||
// Text watermark path
|
||||
if (watermarkParams.parameters.watermarkType === 'text') {
|
||||
// Step 2: Wording
|
||||
steps.push({
|
||||
title: t("watermark.steps.wording", "Wording"),
|
||||
isCollapsed: hasResults,
|
||||
tooltip: watermarkWordingTips,
|
||||
content: (
|
||||
<WatermarkContentSettings
|
||||
<WatermarkWording
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("watermark.steps.style", "Style & Position"),
|
||||
isCollapsed: settingsCollapsed? true : styleCollapsed,
|
||||
});
|
||||
|
||||
// Step 3: Style
|
||||
steps.push({
|
||||
title: t("watermark.steps.textStyle", "Style"),
|
||||
isCollapsed: hasResults ? true : collapsedStyle,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedStyle(!collapsedStyle),
|
||||
tooltip: watermarkStyleTips,
|
||||
tooltip: watermarkTextStyleTips,
|
||||
content: (
|
||||
<WatermarkStyleSettings
|
||||
<WatermarkTextStyle
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("watermark.steps.advanced", "Advanced Options"),
|
||||
isCollapsed: settingsCollapsed? true : advancedCollapsed,
|
||||
});
|
||||
|
||||
// Step 4: Formatting
|
||||
steps.push({
|
||||
title: t("watermark.steps.formatting", "Formatting"),
|
||||
isCollapsed: hasResults ? true : collapsedAdvanced,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedAdvanced(!collapsedAdvanced),
|
||||
tooltip: watermarkAdvancedTips,
|
||||
tooltip: watermarkFormattingTips,
|
||||
content: (
|
||||
<WatermarkAdvancedSettings
|
||||
<WatermarkFormatting
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// Image watermark path
|
||||
if (watermarkParams.parameters.watermarkType === 'image') {
|
||||
// Step 2: Watermark File
|
||||
steps.push({
|
||||
title: t("watermark.steps.file", "Watermark File"),
|
||||
isCollapsed: false,
|
||||
tooltip: watermarkFileTips,
|
||||
content: (
|
||||
<WatermarkFile
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
// Step 3: Formatting
|
||||
steps.push({
|
||||
title: t("watermark.steps.formatting", "Formatting"),
|
||||
isCollapsed: hasResults ? true : collapsedAdvanced,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedAdvanced(!collapsedAdvanced),
|
||||
tooltip: watermarkFormattingTips,
|
||||
content: (
|
||||
<WatermarkFormatting
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return steps;
|
||||
};
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles,
|
||||
isCollapsed: hasFiles || hasResults,
|
||||
},
|
||||
],
|
||||
steps: getSteps(),
|
||||
executeButton: {
|
||||
text: t("watermark.submit", "Add Watermark"),
|
||||
isVisible: !hasResults,
|
||||
|
Loading…
x
Reference in New Issue
Block a user