chore: service updated to match requirements

This commit is contained in:
Chesterkxng
2026-04-26 18:18:44 +02:00
parent 90c185575c
commit 3146f74b60
@@ -2,168 +2,181 @@ import { areColorsSimilar } from '@utils/color';
export const processImage = async (
file: File,
fromColor: [number, number, number],
toColor: [number, number, number],
similarity: number,
setResult: (result: File | null) => void
): Promise<void> => {
fromColor: [number, number, number, number],
toColor: [number, number, number, number],
similarity: number
): Promise<File> => {
if (file.type === 'image/svg+xml') {
const reader = new FileReader();
reader.onload = (e) => {
if (!e.target?.result) return;
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
if (!e.target?.result) return;
let svgContent = e.target.result as string;
const toColorHex = rgbToHex(toColor[0], toColor[1], toColor[2]);
let svgContent = e.target.result as string;
const toColorHex = rgbToHex(
toColor[0],
toColor[1],
toColor[2],
toColor[3]
);
// Replace hex colors with various formats (#fff, #ffffff)
const hexRegexShort = new RegExp(`#[0-9a-f]{3}\\b`, 'gi');
const hexRegexLong = new RegExp(`#[0-9a-f]{6}\\b`, 'gi');
const hexRegexShort = new RegExp(`#[0-9a-f]{3}\\b`, 'gi');
const hexRegexLong = new RegExp(`#[0-9a-f]{6}\\b`, 'gi');
const hexRegexAlpha = new RegExp(`#[0-9a-f]{8}\\b`, 'gi');
svgContent = svgContent.replace(hexRegexShort, (match) => {
// Expand short hex to full form for comparison
const expanded =
'#' + match[1] + match[1] + match[2] + match[2] + match[3] + match[3];
const matchRgb = hexToRgb(expanded);
if (matchRgb && areColorsSimilar(matchRgb, fromColor, similarity)) {
return toColorHex;
}
return match;
});
svgContent = svgContent.replace(hexRegexShort, (match) => {
const expanded =
'#' +
match[1] +
match[1] +
match[2] +
match[2] +
match[3] +
match[3];
const matchRgb = hexToRgb(expanded);
if (matchRgb && areColorsSimilar(matchRgb, fromColor, similarity))
return toColorHex;
return match;
});
svgContent = svgContent.replace(hexRegexLong, (match) => {
const matchRgb = hexToRgb(match);
if (matchRgb && areColorsSimilar(matchRgb, fromColor, similarity)) {
return toColorHex;
}
return match;
});
svgContent = svgContent.replace(hexRegexLong, (match) => {
const matchRgb = hexToRgb(match);
if (matchRgb && areColorsSimilar(matchRgb, fromColor, similarity))
return toColorHex;
return match;
});
// Replace RGB colors
const rgbRegex = new RegExp(
`rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)`,
'gi'
);
svgContent = svgContent.replace(rgbRegex, (match, r, g, b) => {
const matchRgb: [number, number, number] = [
parseInt(r),
parseInt(g),
parseInt(b)
];
if (areColorsSimilar(matchRgb, fromColor, similarity)) {
return `rgb(${toColor[0]}, ${toColor[1]}, ${toColor[2]})`;
}
return match;
});
svgContent = svgContent.replace(hexRegexAlpha, (match) => {
const matchRgba = hexToRgba(match);
if (matchRgba && areColorsSimilar(matchRgba, fromColor, similarity))
return toColorHex;
return match;
});
// Replace RGBA colors (preserving alpha)
const rgbaRegex = new RegExp(
`rgba\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([\\d.]+)\\s*\\)`,
'gi'
);
svgContent = svgContent.replace(rgbaRegex, (match, r, g, b, a) => {
const matchRgb: [number, number, number] = [
parseInt(r),
parseInt(g),
parseInt(b)
];
if (areColorsSimilar(matchRgb, fromColor, similarity)) {
return `rgba(${toColor[0]}, ${toColor[1]}, ${toColor[2]}, ${a})`;
}
return match;
});
const rgbRegex = new RegExp(
`rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)`,
'gi'
);
svgContent = svgContent.replace(rgbRegex, (match, r, g, b) => {
const matchRgb: [number, number, number, number] = [
parseInt(r),
parseInt(g),
parseInt(b),
255
];
if (areColorsSimilar(matchRgb, fromColor, similarity)) {
return `rgb(${toColor[0]}, ${toColor[1]}, ${toColor[2]})`;
}
return match;
});
// Replace named SVG colors if they match our target color
const namedColors = {
red: [255, 0, 0],
green: [0, 128, 0],
blue: [0, 0, 255],
black: [0, 0, 0],
white: [255, 255, 255]
// Add more named colors as needed
const rgbaRegex = new RegExp(
`rgba\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([\\d.]+)\\s*\\)`,
'gi'
);
svgContent = svgContent.replace(rgbaRegex, (match, r, g, b, a) => {
const matchRgba: [number, number, number, number] = [
parseInt(r),
parseInt(g),
parseInt(b),
Math.round(parseFloat(a) * 255)
];
if (areColorsSimilar(matchRgba, fromColor, similarity)) {
return `rgba(${toColor[0]}, ${toColor[1]}, ${toColor[2]}, ${
toColor[3] / 255
})`;
}
return match;
});
const namedColors: Record<string, [number, number, number, number]> = {
red: [255, 0, 0, 255],
green: [0, 128, 0, 255],
blue: [0, 0, 255, 255],
black: [0, 0, 0, 255],
white: [255, 255, 255, 255]
};
Object.entries(namedColors).forEach(([name, rgba]) => {
if (areColorsSimilar(rgba, fromColor, similarity)) {
const colorRegex = new RegExp(`\\b${name}\\b`, 'gi');
svgContent = svgContent.replace(colorRegex, toColorHex);
}
});
resolve(new File([svgContent], file.name, { type: 'image/svg+xml' }));
};
Object.entries(namedColors).forEach(([name, rgb]) => {
if (
areColorsSimilar(
rgb as [number, number, number],
fromColor,
similarity
)
) {
const colorRegex = new RegExp(`\\b${name}\\b`, 'gi');
svgContent = svgContent.replace(colorRegex, toColorHex);
}
});
// Create new file with modified content
const newFile = new File([svgContent], file.name, {
type: 'image/svg+xml'
});
setResult(newFile);
};
reader.readAsText(file);
return;
reader.readAsText(file);
});
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (ctx == null) return;
const img = new Image();
if (!ctx) throw new Error('Canvas not supported');
img.src = URL.createObjectURL(file);
const img = new Image();
const url = URL.createObjectURL(file);
img.src = url;
await img.decode();
URL.revokeObjectURL(url);
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data: Uint8ClampedArray = imageData.data;
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const currentColor: [number, number, number] = [
const currentColor: [number, number, number, number] = [
data[i],
data[i + 1],
data[i + 2]
data[i + 2],
data[i + 3]
];
if (areColorsSimilar(currentColor, fromColor, similarity)) {
data[i] = toColor[0]; // Red
data[i + 1] = toColor[1]; // Green
data[i + 2] = toColor[2]; // Blue
data[i] = toColor[0];
data[i + 1] = toColor[1];
data[i + 2] = toColor[2];
data[i + 3] = toColor[3];
}
}
ctx.putImageData(imageData, 0, 0);
canvas.toBlob((blob) => {
if (blob) {
const newFile = new File([blob], file.name, {
type: file.type
});
setResult(newFile);
}
}, file.type);
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) resolve(new File([blob], file.name, { type: file.type }));
else reject(new Error('Failed to create blob'));
}, file.type);
});
};
const rgbToHex = (r: number, g: number, b: number): string => {
return (
'#' +
[r, g, b]
.map((x) => {
const hex = x.toString(16);
return hex.length === 1 ? '0' + hex : hex;
})
.join('')
);
};
const rgbToHex = (r: number, g: number, b: number, a = 255): string =>
'#' + [r, g, b, a].map((x) => x.toString(16).padStart(2, '0')).join('');
// Helper function to parse hex to RGB
const hexToRgb = (hex: string): [number, number, number] | null => {
const hexToRgb = (hex: string): [number, number, number, number] | null => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
parseInt(result[3], 16),
255
]
: null;
};
const hexToRgba = (hex: string): [number, number, number, number] | null => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
hex
);
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16),
parseInt(result[4], 16)
]
: null;
};