mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-26 17:52:59 +02:00
invert!
This commit is contained in:
parent
21b1428ab5
commit
6e6e77fb85
@ -621,6 +621,10 @@
|
|||||||
"title": "Replace & Invert Colour",
|
"title": "Replace & Invert Colour",
|
||||||
"desc": "Replace or invert colours in PDF documents"
|
"desc": "Replace or invert colours in PDF documents"
|
||||||
},
|
},
|
||||||
|
"replaceColor": {
|
||||||
|
"title": "Replace & Invert Colour",
|
||||||
|
"desc": "Replace or invert colours in PDF documents"
|
||||||
|
},
|
||||||
"devApi": {
|
"devApi": {
|
||||||
"tags": "API,development,documentation",
|
"tags": "API,development,documentation",
|
||||||
"title": "API",
|
"title": "API",
|
||||||
@ -2509,7 +2513,7 @@
|
|||||||
"3": "Custom(Customised colours)",
|
"3": "Custom(Customised colours)",
|
||||||
"4": "Full-Invert(Invert all colours)",
|
"4": "Full-Invert(Invert all colours)",
|
||||||
"5": "High contrast colour options",
|
"5": "High contrast colour options",
|
||||||
"6": "white text on black background",
|
"6": "White text on black background",
|
||||||
"7": "Black text on white background",
|
"7": "Black text on white background",
|
||||||
"8": "Yellow text on black background",
|
"8": "Yellow text on black background",
|
||||||
"9": "Green text on black background",
|
"9": "Green text on black background",
|
||||||
@ -2518,6 +2522,47 @@
|
|||||||
},
|
},
|
||||||
"submit": "Replace"
|
"submit": "Replace"
|
||||||
},
|
},
|
||||||
|
"replaceColor": {
|
||||||
|
"labels": {
|
||||||
|
"settings": "Settings",
|
||||||
|
"colourOperation": "Colour operation"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"highContrast": "High contrast",
|
||||||
|
"invertAll": "Invert all colours",
|
||||||
|
"custom": "Custom"
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"header": {
|
||||||
|
"title": "Replace & Invert Colour Settings Overview"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"title": "Description",
|
||||||
|
"text": "Transform PDF colours to improve readability and accessibility. Choose from high contrast presets, invert all colours, or create custom colour schemes."
|
||||||
|
},
|
||||||
|
"highContrast": {
|
||||||
|
"title": "High Contrast",
|
||||||
|
"text": "Apply predefined high contrast colour combinations designed for better readability and accessibility compliance.",
|
||||||
|
"bullet1": "White text on black background - Classic dark mode",
|
||||||
|
"bullet2": "Black text on white background - Standard high contrast",
|
||||||
|
"bullet3": "Yellow text on black background - High visibility option",
|
||||||
|
"bullet4": "Green text on black background - Alternative high contrast"
|
||||||
|
},
|
||||||
|
"invertAll": {
|
||||||
|
"title": "Invert All Colours",
|
||||||
|
"text": "Completely invert all colours in the PDF, creating a negative-like effect. Useful for creating dark mode versions of documents or reducing eye strain in low-light conditions."
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"title": "Custom Colours",
|
||||||
|
"text": "Define your own text and background colours using the colour pickers. Perfect for creating branded documents or specific accessibility requirements.",
|
||||||
|
"bullet1": "Text colour - Choose the colour for text elements",
|
||||||
|
"bullet2": "Background colour - Set the background colour for the document"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"failed": "An error occurred while processing the colour replacement."
|
||||||
|
}
|
||||||
|
},
|
||||||
"replaceColorPdf": {
|
"replaceColorPdf": {
|
||||||
"tags": "Replace Colour,Page operations,Back end,server side"
|
"tags": "Replace Colour,Page operations,Back end,server side"
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Stack, Text, Select, ColorInput } from "@mantine/core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { ReplaceColorParameters } from "../../../hooks/tools/replaceColor/useReplaceColorParameters";
|
||||||
|
|
||||||
|
interface ReplaceColorSettingsProps {
|
||||||
|
parameters: ReplaceColorParameters;
|
||||||
|
onParameterChange: <K extends keyof ReplaceColorParameters>(key: K, value: ReplaceColorParameters[K]) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReplaceColorSettings = ({ parameters, onParameterChange, disabled = false }: ReplaceColorSettingsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const replaceAndInvertOptions = [
|
||||||
|
{
|
||||||
|
value: 'HIGH_CONTRAST_COLOR',
|
||||||
|
label: t('replaceColor.options.highContrast', 'High contrast')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'FULL_INVERSION',
|
||||||
|
label: t('replaceColor.options.invertAll', 'Invert all colours')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'CUSTOM_COLOR',
|
||||||
|
label: t('replaceColor.options.custom', 'Custom')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const highContrastOptions = [
|
||||||
|
{
|
||||||
|
value: 'WHITE_TEXT_ON_BLACK',
|
||||||
|
label: t('replace-color.selectText.6', 'White text on black background')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'BLACK_TEXT_ON_WHITE',
|
||||||
|
label: t('replace-color.selectText.7', 'Black text on white background')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'YELLOW_TEXT_ON_BLACK',
|
||||||
|
label: t('replace-color.selectText.8', 'Yellow text on black background')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'GREEN_TEXT_ON_BLACK',
|
||||||
|
label: t('replace-color.selectText.9', 'Green text on black background')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="md">
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('replaceColor.labels.colourOperation', 'Colour operation')}
|
||||||
|
</Text>
|
||||||
|
<Select
|
||||||
|
value={parameters.replaceAndInvertOption}
|
||||||
|
onChange={(value) => value && onParameterChange('replaceAndInvertOption', value as ReplaceColorParameters['replaceAndInvertOption'])}
|
||||||
|
data={replaceAndInvertOptions}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{parameters.replaceAndInvertOption === 'HIGH_CONTRAST_COLOR' && (
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('replace-color.selectText.5', 'High contrast color options')}
|
||||||
|
</Text>
|
||||||
|
<Select
|
||||||
|
value={parameters.highContrastColorCombination}
|
||||||
|
onChange={(value) => value && onParameterChange('highContrastColorCombination', value as ReplaceColorParameters['highContrastColorCombination'])}
|
||||||
|
data={highContrastOptions}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{parameters.replaceAndInvertOption === 'CUSTOM_COLOR' && (
|
||||||
|
<>
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('replace-color.selectText.10', 'Choose text Color')}
|
||||||
|
</Text>
|
||||||
|
<ColorInput
|
||||||
|
value={parameters.textColor}
|
||||||
|
onChange={(value) => onParameterChange('textColor', value)}
|
||||||
|
format="hex"
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('replace-color.selectText.11', 'Choose background Color')}
|
||||||
|
</Text>
|
||||||
|
<ColorInput
|
||||||
|
value={parameters.backGroundColor}
|
||||||
|
onChange={(value) => onParameterChange('backGroundColor', value)}
|
||||||
|
format="hex"
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReplaceColorSettings;
|
40
frontend/src/components/tooltips/useReplaceColorTips.ts
Normal file
40
frontend/src/components/tooltips/useReplaceColorTips.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { TooltipContent } from '../../types/tips';
|
||||||
|
|
||||||
|
export const useReplaceColorTips = (): TooltipContent => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return {
|
||||||
|
header: {
|
||||||
|
title: t("replaceColor.tooltip.header.title", "Replace & Invert Colour Settings Overview")
|
||||||
|
},
|
||||||
|
tips: [
|
||||||
|
{
|
||||||
|
title: t("replaceColor.tooltip.description.title", "Description"),
|
||||||
|
description: t("replaceColor.tooltip.description.text", "Transform PDF colours to improve readability and accessibility. Choose from high contrast presets, invert all colours, or create custom colour schemes.")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("replaceColor.tooltip.highContrast.title", "High Contrast"),
|
||||||
|
description: t("replaceColor.tooltip.highContrast.text", "Apply predefined high contrast colour combinations designed for better readability and accessibility compliance."),
|
||||||
|
bullets: [
|
||||||
|
t("replaceColor.tooltip.highContrast.bullet1", "White text on black background - Classic dark mode"),
|
||||||
|
t("replaceColor.tooltip.highContrast.bullet2", "Black text on white background - Standard high contrast"),
|
||||||
|
t("replaceColor.tooltip.highContrast.bullet3", "Yellow text on black background - High visibility option"),
|
||||||
|
t("replaceColor.tooltip.highContrast.bullet4", "Green text on black background - Alternative high contrast")
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("replaceColor.tooltip.invertAll.title", "Invert All Colours"),
|
||||||
|
description: t("replaceColor.tooltip.invertAll.text", "Completely invert all colours in the PDF, creating a negative-like effect. Useful for creating dark mode versions of documents or reducing eye strain in low-light conditions.")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("replaceColor.tooltip.custom.title", "Custom Colours"),
|
||||||
|
description: t("replaceColor.tooltip.custom.text", "Define your own text and background colours using the colour pickers. Perfect for creating branded documents or specific accessibility requirements."),
|
||||||
|
bullets: [
|
||||||
|
t("replaceColor.tooltip.custom.bullet1", "Text colour - Choose the colour for text elements"),
|
||||||
|
t("replaceColor.tooltip.custom.bullet2", "Background colour - Set the background colour for the document")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
@ -50,6 +50,7 @@ import { redactOperationConfig } from "../hooks/tools/redact/useRedactOperation"
|
|||||||
import { rotateOperationConfig } from "../hooks/tools/rotate/useRotateOperation";
|
import { rotateOperationConfig } from "../hooks/tools/rotate/useRotateOperation";
|
||||||
import { changeMetadataOperationConfig } from "../hooks/tools/changeMetadata/useChangeMetadataOperation";
|
import { changeMetadataOperationConfig } from "../hooks/tools/changeMetadata/useChangeMetadataOperation";
|
||||||
import { cropOperationConfig } from "../hooks/tools/crop/useCropOperation";
|
import { cropOperationConfig } from "../hooks/tools/crop/useCropOperation";
|
||||||
|
import { replaceColorOperationConfig } from "../hooks/tools/replaceColor/useReplaceColorOperation";
|
||||||
import CompressSettings from "../components/tools/compress/CompressSettings";
|
import CompressSettings from "../components/tools/compress/CompressSettings";
|
||||||
import SplitSettings from "../components/tools/split/SplitSettings";
|
import SplitSettings from "../components/tools/split/SplitSettings";
|
||||||
import AddPasswordSettings from "../components/tools/addPassword/AddPasswordSettings";
|
import AddPasswordSettings from "../components/tools/addPassword/AddPasswordSettings";
|
||||||
@ -68,12 +69,14 @@ import RedactSingleStepSettings from "../components/tools/redact/RedactSingleSte
|
|||||||
import RotateSettings from "../components/tools/rotate/RotateSettings";
|
import RotateSettings from "../components/tools/rotate/RotateSettings";
|
||||||
import Redact from "../tools/Redact";
|
import Redact from "../tools/Redact";
|
||||||
import AdjustPageScale from "../tools/AdjustPageScale";
|
import AdjustPageScale from "../tools/AdjustPageScale";
|
||||||
|
import ReplaceColor from "../tools/ReplaceColor";
|
||||||
import { ToolId } from "../types/toolId";
|
import { ToolId } from "../types/toolId";
|
||||||
import MergeSettings from '../components/tools/merge/MergeSettings';
|
import MergeSettings from '../components/tools/merge/MergeSettings';
|
||||||
import { adjustPageScaleOperationConfig } from "../hooks/tools/adjustPageScale/useAdjustPageScaleOperation";
|
import { adjustPageScaleOperationConfig } from "../hooks/tools/adjustPageScale/useAdjustPageScaleOperation";
|
||||||
import AdjustPageScaleSettings from "../components/tools/adjustPageScale/AdjustPageScaleSettings";
|
import AdjustPageScaleSettings from "../components/tools/adjustPageScale/AdjustPageScaleSettings";
|
||||||
import ChangeMetadataSingleStep from "../components/tools/changeMetadata/ChangeMetadataSingleStep";
|
import ChangeMetadataSingleStep from "../components/tools/changeMetadata/ChangeMetadataSingleStep";
|
||||||
import CropSettings from "../components/tools/crop/CropSettings";
|
import CropSettings from "../components/tools/crop/CropSettings";
|
||||||
|
import ReplaceColorSettings from "../components/tools/replaceColor/ReplaceColorSettings";
|
||||||
|
|
||||||
const showPlaceholderTools = true; // Show all tools; grey out unavailable ones in UI
|
const showPlaceholderTools = true; // Show all tools; grey out unavailable ones in UI
|
||||||
|
|
||||||
@ -642,10 +645,14 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
replaceColorPdf: {
|
replaceColorPdf: {
|
||||||
icon: <LocalIcon icon="format-color-fill-rounded" width="1.5rem" height="1.5rem" />,
|
icon: <LocalIcon icon="format-color-fill-rounded" width="1.5rem" height="1.5rem" />,
|
||||||
name: t("home.replaceColorPdf.title", "Replace & Invert Color"),
|
name: t("home.replaceColorPdf.title", "Replace & Invert Color"),
|
||||||
component: null,
|
component: ReplaceColor,
|
||||||
description: t("home.replaceColorPdf.desc", "Replace or invert colors in PDF documents"),
|
description: t("home.replaceColorPdf.desc", "Replace or invert colors in PDF documents"),
|
||||||
categoryId: ToolCategoryId.ADVANCED_TOOLS,
|
categoryId: ToolCategoryId.ADVANCED_TOOLS,
|
||||||
subcategoryId: SubcategoryId.ADVANCED_FORMATTING,
|
subcategoryId: SubcategoryId.ADVANCED_FORMATTING,
|
||||||
|
maxFiles: -1,
|
||||||
|
endpoints: ["replace-invert-pdf"],
|
||||||
|
operationConfig: replaceColorOperationConfig,
|
||||||
|
settingsComponent: ReplaceColorSettings,
|
||||||
synonyms: getSynonyms(t, "replaceColorPdf"),
|
synonyms: getSynonyms(t, "replaceColorPdf"),
|
||||||
},
|
},
|
||||||
addImage: {
|
addImage: {
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { ToolType, useToolOperation } from '../shared/useToolOperation';
|
||||||
|
import { createStandardErrorHandler } from '../../../utils/toolErrorHandler';
|
||||||
|
import { ReplaceColorParameters, defaultParameters } from './useReplaceColorParameters';
|
||||||
|
|
||||||
|
export const buildReplaceColorFormData = (parameters: ReplaceColorParameters, file: File): FormData => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('fileInput', file);
|
||||||
|
|
||||||
|
formData.append('replaceAndInvertOption', parameters.replaceAndInvertOption);
|
||||||
|
|
||||||
|
if (parameters.replaceAndInvertOption === 'HIGH_CONTRAST_COLOR') {
|
||||||
|
formData.append('highContrastColorCombination', parameters.highContrastColorCombination);
|
||||||
|
} else if (parameters.replaceAndInvertOption === 'CUSTOM_COLOR') {
|
||||||
|
formData.append('textColor', parameters.textColor);
|
||||||
|
formData.append('backGroundColor', parameters.backGroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const replaceColorOperationConfig = {
|
||||||
|
toolType: ToolType.singleFile,
|
||||||
|
buildFormData: buildReplaceColorFormData,
|
||||||
|
operationType: 'replaceColor',
|
||||||
|
endpoint: '/api/v1/misc/replace-invert-pdf',
|
||||||
|
multiFileEndpoint: false,
|
||||||
|
defaultParameters,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const useReplaceColorOperation = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return useToolOperation<ReplaceColorParameters>({
|
||||||
|
...replaceColorOperationConfig,
|
||||||
|
getErrorMessage: createStandardErrorHandler(t('replaceColor.error.failed', 'An error occurred while processing the color replacement.'))
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,29 @@
|
|||||||
|
import { BaseParameters } from '../../../types/parameters';
|
||||||
|
import { useBaseParameters, BaseParametersHook } from '../shared/useBaseParameters';
|
||||||
|
|
||||||
|
export interface ReplaceColorParameters extends BaseParameters {
|
||||||
|
replaceAndInvertOption: 'HIGH_CONTRAST_COLOR' | 'CUSTOM_COLOR' | 'FULL_INVERSION';
|
||||||
|
highContrastColorCombination: 'WHITE_TEXT_ON_BLACK' | 'BLACK_TEXT_ON_WHITE' | 'YELLOW_TEXT_ON_BLACK' | 'GREEN_TEXT_ON_BLACK';
|
||||||
|
textColor: string;
|
||||||
|
backGroundColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultParameters: ReplaceColorParameters = {
|
||||||
|
replaceAndInvertOption: 'HIGH_CONTRAST_COLOR',
|
||||||
|
highContrastColorCombination: 'WHITE_TEXT_ON_BLACK',
|
||||||
|
textColor: '#000000',
|
||||||
|
backGroundColor: '#ffffff',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReplaceColorParametersHook = BaseParametersHook<ReplaceColorParameters>;
|
||||||
|
|
||||||
|
export const useReplaceColorParameters = (): ReplaceColorParametersHook => {
|
||||||
|
return useBaseParameters({
|
||||||
|
defaultParameters,
|
||||||
|
endpointName: 'replace-invert-pdf',
|
||||||
|
validateFn: (params) => {
|
||||||
|
// All parameters are always valid as they have defaults
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
58
frontend/src/tools/ReplaceColor.tsx
Normal file
58
frontend/src/tools/ReplaceColor.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||||
|
import ReplaceColorSettings from "../components/tools/replaceColor/ReplaceColorSettings";
|
||||||
|
import { useReplaceColorParameters } from "../hooks/tools/replaceColor/useReplaceColorParameters";
|
||||||
|
import { useReplaceColorOperation } from "../hooks/tools/replaceColor/useReplaceColorOperation";
|
||||||
|
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
|
||||||
|
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||||
|
import { useReplaceColorTips } from "../components/tooltips/useReplaceColorTips";
|
||||||
|
|
||||||
|
const ReplaceColor = (props: BaseToolProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const replaceColorTips = useReplaceColorTips();
|
||||||
|
|
||||||
|
const base = useBaseTool(
|
||||||
|
'replaceColor',
|
||||||
|
useReplaceColorParameters,
|
||||||
|
useReplaceColorOperation,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
|
||||||
|
return createToolFlow({
|
||||||
|
files: {
|
||||||
|
selectedFiles: base.selectedFiles,
|
||||||
|
isCollapsed: base.hasResults,
|
||||||
|
},
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
title: t("replaceColor.labels.settings", "Settings"),
|
||||||
|
isCollapsed: base.settingsCollapsed,
|
||||||
|
onCollapsedClick: base.settingsCollapsed ? base.handleSettingsReset : undefined,
|
||||||
|
tooltip: replaceColorTips,
|
||||||
|
content: (
|
||||||
|
<ReplaceColorSettings
|
||||||
|
parameters={base.params.parameters}
|
||||||
|
onParameterChange={base.params.updateParameter}
|
||||||
|
disabled={base.endpointLoading}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
executeButton: {
|
||||||
|
text: t("replace-color.submit", "Replace"),
|
||||||
|
isVisible: !base.hasResults,
|
||||||
|
loadingText: t("loading"),
|
||||||
|
onClick: base.handleExecute,
|
||||||
|
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||||
|
},
|
||||||
|
review: {
|
||||||
|
isVisible: base.hasResults,
|
||||||
|
operation: base.operation,
|
||||||
|
title: t("replace-color.title", "Replace-Invert-Color"),
|
||||||
|
onFileClick: base.handleThumbnailClick,
|
||||||
|
onUndo: base.handleUndo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReplaceColor as ToolComponent;
|
Loading…
Reference in New Issue
Block a user