+
+
+
+
+
{t('tools.ocr.title')}
+
{t('tools.ocr.description')}
+
+
+
+
+ {phase === 'upload' && (
+
+ {/* Mode selector */}
+
+
+
+ {(['image', 'pdf'] as OcrMode[]).map((m) => (
+
+ ))}
+
+
+
+
+
+ {file && !isUploading && (
+ <>
+ {/* Language selector */}
+
+
+
+ {LANGUAGES.map((l) => (
+
+ ))}
+
+
+
+
+ >
+ )}
+
+ )}
+
+ {phase === 'processing' && (
+
+
+ {taskError && (
+
+ {taskError}
+
+ )}
+
+ )}
+
+ {phase === 'done' && result?.status === 'completed' && (
+
+
+
+ {t('tools.ocr.charsExtracted', { count: result.char_count ?? 0 })}
+
+
+
+
+
+
+ {result.download_url && (
+
+ )}
+
+
+ )}
+
+ {phase === 'done' && result?.status === 'failed' && (
+
+
+ {result.error || t('common.genericError')}
+
+
+
+ )}
+
+
+
+ >
+ );
+}
diff --git a/frontend/src/components/tools/RemoveBackground.tsx b/frontend/src/components/tools/RemoveBackground.tsx
new file mode 100644
index 0000000..77a82d8
--- /dev/null
+++ b/frontend/src/components/tools/RemoveBackground.tsx
@@ -0,0 +1,150 @@
+import { useState, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Helmet } from 'react-helmet-async';
+import { Eraser } from 'lucide-react';
+import FileUploader from '@/components/shared/FileUploader';
+import ProgressBar from '@/components/shared/ProgressBar';
+import DownloadButton from '@/components/shared/DownloadButton';
+import AdSlot from '@/components/layout/AdSlot';
+import { useFileUpload } from '@/hooks/useFileUpload';
+import { useTaskPolling } from '@/hooks/useTaskPolling';
+import { generateToolSchema } from '@/utils/seo';
+import { useFileStore } from '@/stores/fileStore';
+import { useConfig } from '@/hooks/useConfig';
+
+export default function RemoveBackground() {
+ const { t } = useTranslation();
+ const { limits } = useConfig();
+ const [phase, setPhase] = useState<'upload' | 'processing' | 'done'>('upload');
+
+ const {
+ file, uploadProgress, isUploading, taskId,
+ error: uploadError, selectFile, startUpload, reset,
+ } = useFileUpload({
+ endpoint: '/remove-bg',
+ maxSizeMB: limits.image ?? 10,
+ acceptedTypes: ['png', 'jpg', 'jpeg', 'webp'],
+ });
+
+ const { status, result, error: taskError } = useTaskPolling({
+ taskId,
+ onComplete: () => setPhase('done'),
+ onError: () => setPhase('done'),
+ });
+
+ // Accept file from homepage smart upload
+ const storeFile = useFileStore((s) => s.file);
+ const clearStoreFile = useFileStore((s) => s.clearFile);
+ useEffect(() => {
+ if (storeFile) {
+ selectFile(storeFile);
+ clearStoreFile();
+ }
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
+
+ const handleUpload = async () => {
+ const id = await startUpload();
+ if (id) setPhase('processing');
+ };
+
+ const handleReset = () => {
+ reset();
+ setPhase('upload');
+ };
+
+ const schema = generateToolSchema({
+ name: t('tools.removeBg.title'),
+ description: t('tools.removeBg.description'),
+ url: `${window.location.origin}/tools/remove-background`,
+ });
+
+ return (
+ <>
+
+
+
+
+
+
{t('tools.removeBg.title')}
+
{t('tools.removeBg.description')}
+
+
+
+
+ {phase === 'upload' && (
+
+
+
+ {file && !isUploading && (
+
+ )}
+
+ )}
+
+ {phase === 'processing' && (
+
+
+ {taskError && (
+
+ {taskError}
+
+ )}
+
+ )}
+
+ {phase === 'done' && result?.status === 'completed' && (
+
+
+
+ {t('tools.removeBg.success')}
+
+
+
+
+
+ )}
+
+ {phase === 'done' && result?.status === 'failed' && (
+
+
+ {result.error || t('common.genericError')}
+
+
+
+ )}
+
+
+
+ >
+ );
+}
diff --git a/frontend/src/i18n/ar.json b/frontend/src/i18n/ar.json
index 1713604..3a367d2 100644
--- a/frontend/src/i18n/ar.json
+++ b/frontend/src/i18n/ar.json
@@ -110,6 +110,25 @@
"lockAspect": "قفل نسبة العرض للارتفاع",
"aspectHint": "أدخل بُعداً واحداً — سيتم حساب الآخر تلقائياً للحفاظ على نسبة العرض للارتفاع."
},
+ "ocr": {
+ "title": "OCR — التعرف على النصوص",
+ "description": "استخرج النصوص من الصور ومستندات PDF الممسوحة ضوئياً باستخدام التعرف الضوئي على الحروف.",
+ "shortDesc": "استخراج نص",
+ "sourceType": "نوع المصدر",
+ "modeImage": "صورة",
+ "modePdf": "PDF",
+ "language": "لغة التعرف",
+ "extract": "استخراج النص",
+ "charsExtracted": "تم استخراج {{count}} حرف",
+ "copyText": "نسخ النص"
+ },
+ "removeBg": {
+ "title": "إزالة الخلفية",
+ "description": "أزل خلفية الصور تلقائياً بالذكاء الاصطناعي. احصل على صورة PNG شفافة في ثوانٍ.",
+ "shortDesc": "إزالة الخلفية",
+ "remove": "إزالة الخلفية",
+ "success": "تمت إزالة الخلفية بنجاح!"
+ },
"videoToGif": {
"title": "فيديو إلى GIF",
"description": "أنشئ صور GIF متحركة من مقاطع الفيديو. خصّص وقت البداية والمدة والجودة.",
diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json
index 7f4cd91..d003121 100644
--- a/frontend/src/i18n/en.json
+++ b/frontend/src/i18n/en.json
@@ -110,6 +110,25 @@
"lockAspect": "Lock aspect ratio",
"aspectHint": "Enter one dimension — the other will auto-calculate to preserve aspect ratio."
},
+ "ocr": {
+ "title": "OCR — Text Recognition",
+ "description": "Extract text from images and scanned PDF documents using optical character recognition.",
+ "shortDesc": "Extract Text",
+ "sourceType": "Source Type",
+ "modeImage": "Image",
+ "modePdf": "PDF",
+ "language": "OCR Language",
+ "extract": "Extract Text",
+ "charsExtracted": "{{count}} characters extracted",
+ "copyText": "Copy Text"
+ },
+ "removeBg": {
+ "title": "Remove Background",
+ "description": "Remove the background from images automatically using AI. Get a transparent PNG in seconds.",
+ "shortDesc": "Remove BG",
+ "remove": "Remove Background",
+ "success": "Background removed successfully!"
+ },
"videoToGif": {
"title": "Video to GIF",
"description": "Create animated GIFs from video clips. Customize start time, duration, and quality.",
diff --git a/frontend/src/i18n/fr.json b/frontend/src/i18n/fr.json
index fc68e5d..26f7a31 100644
--- a/frontend/src/i18n/fr.json
+++ b/frontend/src/i18n/fr.json
@@ -110,6 +110,25 @@
"lockAspect": "Verrouiller le rapport d'aspect",
"aspectHint": "Entrez une dimension — l'autre sera calculée automatiquement pour préserver le rapport d'aspect."
},
+ "ocr": {
+ "title": "OCR — Reconnaissance de texte",
+ "description": "Extrayez le texte des images et des documents PDF numérisés grâce à la reconnaissance optique de caractères.",
+ "shortDesc": "Extraire le texte",
+ "sourceType": "Type de source",
+ "modeImage": "Image",
+ "modePdf": "PDF",
+ "language": "Langue OCR",
+ "extract": "Extraire le texte",
+ "charsExtracted": "{{count}} caractères extraits",
+ "copyText": "Copier le texte"
+ },
+ "removeBg": {
+ "title": "Supprimer l'arrière-plan",
+ "description": "Supprimez l'arrière-plan des images automatiquement grâce à l'IA. Obtenez un PNG transparent en quelques secondes.",
+ "shortDesc": "Suppr. arrière-plan",
+ "remove": "Supprimer l'arrière-plan",
+ "success": "Arrière-plan supprimé avec succès !"
+ },
"videoToGif": {
"title": "Vidéo en GIF",
"description": "Créez des GIFs animés à partir de clips vidéo. Personnalisez le temps de début, la durée et la qualité.",
diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx
index d96222a..3165bbe 100644
--- a/frontend/src/pages/HomePage.tsx
+++ b/frontend/src/pages/HomePage.tsx
@@ -20,6 +20,7 @@ import {
PenLine,
GitBranch,
Scaling,
+ ScanText,
} from 'lucide-react';
import ToolCard from '@/components/shared/ToolCard';
import HeroUploadZone from '@/components/shared/HeroUploadZone';
@@ -52,6 +53,8 @@ const pdfTools: ToolInfo[] = [
const otherTools: ToolInfo[] = [
{ key: 'imageConvert', path: '/tools/image-converter', icon: