diff --git a/frontend/src/components/tools/convert/ConvertSettings.tsx b/frontend/src/components/tools/convert/ConvertSettings.tsx
index 6907a10f5..03c45b019 100644
--- a/frontend/src/components/tools/convert/ConvertSettings.tsx
+++ b/frontend/src/components/tools/convert/ConvertSettings.tsx
@@ -3,6 +3,7 @@ import { Stack, Text, Group, Divider, UnstyledButton, useMantineTheme, useMantin
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import { useTranslation } from "react-i18next";
import { useMultipleEndpointsEnabled } from "../../../hooks/useEndpointConfig";
+import { isImageFormat } from "../../../utils/convertUtils";
import GroupedFormatDropdown from "./GroupedFormatDropdown";
import ConvertToImageSettings from "./ConvertToImageSettings";
import ConvertFromImageSettings from "./ConvertFromImageSettings";
@@ -54,11 +55,20 @@ const ConvertSettings = ({
// Enhanced FROM options with endpoint availability
const enhancedFromOptions = useMemo(() => {
- return FROM_FORMAT_OPTIONS.map(option => ({
- ...option,
- enabled: true // All "from" formats are generally available for selection
- }));
- }, []);
+ return FROM_FORMAT_OPTIONS.map(option => {
+ // Check if this source format has any available conversions
+ const availableConversions = getAvailableToExtensions(option.value) || [];
+ const hasAvailableConversions = availableConversions.some(targetOption =>
+ isConversionAvailable(option.value, targetOption.value)
+ );
+
+
+ return {
+ ...option,
+ enabled: hasAvailableConversions
+ };
+ });
+ }, [getAvailableToExtensions, endpointStatus]);
// Enhanced TO options with endpoint availability
const enhancedToOptions = useMemo(() => {
@@ -69,7 +79,7 @@ const ConvertSettings = ({
...option,
enabled: isConversionAvailable(parameters.fromExtension, option.value)
}));
- }, [parameters.fromExtension, getAvailableToExtensions]);
+ }, [parameters.fromExtension, getAvailableToExtensions, endpointStatus]);
const handleFromExtensionChange = (value: string) => {
onParameterChange('fromExtension', value);
@@ -149,7 +159,7 @@ const ConvertSettings = ({
{/* Format-specific options */}
- {['png', 'jpg'].includes(parameters.toExtension) && (
+ {isImageFormat(parameters.toExtension) && (
<>
>
)}
+
+ {/* EML specific options */}
+ {parameters.fromExtension === 'eml' && parameters.toExtension === 'pdf' && (
+ <>
+
+
+ {t("convert.emlOptions", "Email Options")}:
+
+ {t("convert.emlNote", "Email attachments and embedded images will be included in the PDF conversion.")}
+
+
+ >
+ )}
);
};
diff --git a/frontend/src/constants/convertConstants.ts b/frontend/src/constants/convertConstants.ts
index 709517532..6aba231fd 100644
--- a/frontend/src/constants/convertConstants.ts
+++ b/frontend/src/constants/convertConstants.ts
@@ -18,10 +18,14 @@ export const CONVERSION_ENDPOINTS = {
'pdf-office-word': '/api/v1/convert/pdf/word',
'pdf-office-presentation': '/api/v1/convert/pdf/presentation',
'pdf-office-text': '/api/v1/convert/pdf/text',
+ 'pdf-csv': '/api/v1/convert/pdf/csv',
+ 'pdf-markdown': '/api/v1/convert/pdf/markdown',
'pdf-html': '/api/v1/convert/pdf/html',
'pdf-xml': '/api/v1/convert/pdf/xml',
+ 'pdf-pdfa': '/api/v1/convert/pdf/pdfa',
'html-pdf': '/api/v1/convert/html/pdf',
- 'markdown-pdf': '/api/v1/convert/markdown/pdf'
+ 'markdown-pdf': '/api/v1/convert/markdown/pdf',
+ 'eml-pdf': '/api/v1/convert/eml/pdf'
} as const;
export const ENDPOINT_NAMES = {
@@ -31,10 +35,14 @@ export const ENDPOINT_NAMES = {
'pdf-office-word': 'pdf-to-word',
'pdf-office-presentation': 'pdf-to-presentation',
'pdf-office-text': 'pdf-to-text',
+ 'pdf-csv': 'pdf-to-csv',
+ 'pdf-markdown': 'pdf-to-markdown',
'pdf-html': 'pdf-to-html',
'pdf-xml': 'pdf-to-xml',
+ 'pdf-pdfa': 'pdf-to-pdfa',
'html-pdf': 'html-to-pdf',
- 'markdown-pdf': 'markdown-to-pdf'
+ 'markdown-pdf': 'markdown-to-pdf',
+ 'eml-pdf': 'eml-to-pdf'
} as const;
@@ -62,42 +70,53 @@ export const FROM_FORMAT_OPTIONS = [
{ value: 'md', label: 'MD', group: 'Text' },
{ value: 'txt', label: 'TXT', group: 'Text' },
{ value: 'rtf', label: 'RTF', group: 'Text' },
+ { value: 'eml', label: 'EML', group: 'Email' },
];
export const TO_FORMAT_OPTIONS = [
{ value: 'pdf', label: 'PDF', group: 'Document' },
+ { value: 'pdfa', label: 'PDF/A', group: 'Document' },
{ value: 'docx', label: 'DOCX', group: 'Document' },
{ value: 'odt', label: 'ODT', group: 'Document' },
+ { value: 'csv', label: 'CSV', group: 'Spreadsheet' },
{ value: 'pptx', label: 'PPTX', group: 'Presentation' },
{ value: 'odp', label: 'ODP', group: 'Presentation' },
{ value: 'txt', label: 'TXT', group: 'Text' },
{ value: 'rtf', label: 'RTF', group: 'Text' },
+ { value: 'md', label: 'MD', group: 'Text' },
{ value: 'png', label: 'PNG', group: 'Image' },
{ value: 'jpg', label: 'JPG', group: 'Image' },
+ { value: 'gif', label: 'GIF', group: 'Image' },
+ { value: 'tiff', label: 'TIFF', group: 'Image' },
+ { value: 'bmp', label: 'BMP', group: 'Image' },
+ { value: 'webp', label: 'WEBP', group: 'Image' },
{ value: 'html', label: 'HTML', group: 'Web' },
{ value: 'xml', label: 'XML', group: 'Web' },
];
// Conversion matrix - what each source format can convert to
export const CONVERSION_MATRIX: Record = {
- 'pdf': ['png', 'jpg', 'docx', 'odt', 'pptx', 'odp', 'txt', 'rtf', 'html', 'xml'],
+ 'pdf': ['png', 'jpg', 'gif', 'tiff', 'bmp', 'webp', 'docx', 'odt', 'pptx', 'odp', 'csv', 'txt', 'rtf', 'md', 'html', 'xml', 'pdfa'],
'docx': ['pdf'], 'doc': ['pdf'], 'odt': ['pdf'],
'xlsx': ['pdf'], 'xls': ['pdf'], 'ods': ['pdf'],
'pptx': ['pdf'], 'ppt': ['pdf'], 'odp': ['pdf'],
'jpg': ['pdf'], 'jpeg': ['pdf'], 'png': ['pdf'], 'gif': ['pdf'], 'bmp': ['pdf'], 'tiff': ['pdf'], 'webp': ['pdf'],
'html': ['pdf'], 'htm': ['pdf'],
'md': ['pdf'],
- 'txt': ['pdf'], 'rtf': ['pdf']
+ 'txt': ['pdf'], 'rtf': ['pdf'],
+ 'eml': ['pdf']
};
// Map extensions to endpoint keys
export const EXTENSION_TO_ENDPOINT: Record> = {
'pdf': {
- 'png': 'pdf-to-img', 'jpg': 'pdf-to-img',
+ 'png': 'pdf-to-img', 'jpg': 'pdf-to-img', 'gif': 'pdf-to-img', 'tiff': 'pdf-to-img', 'bmp': 'pdf-to-img', 'webp': 'pdf-to-img',
'docx': 'pdf-to-word', 'odt': 'pdf-to-word',
'pptx': 'pdf-to-presentation', 'odp': 'pdf-to-presentation',
- 'txt': 'pdf-to-text', 'rtf': 'pdf-to-text',
- 'html': 'pdf-to-html', 'xml': 'pdf-to-xml'
+ 'csv': 'pdf-to-csv',
+ 'txt': 'pdf-to-text', 'rtf': 'pdf-to-text', 'md': 'pdf-to-markdown',
+ 'html': 'pdf-to-html', 'xml': 'pdf-to-xml',
+ 'pdfa': 'pdf-to-pdfa'
},
'docx': { 'pdf': 'file-to-pdf' }, 'doc': { 'pdf': 'file-to-pdf' }, 'odt': { 'pdf': 'file-to-pdf' },
'xlsx': { 'pdf': 'file-to-pdf' }, 'xls': { 'pdf': 'file-to-pdf' }, 'ods': { 'pdf': 'file-to-pdf' },
@@ -106,7 +125,8 @@ export const EXTENSION_TO_ENDPOINT: Record> = {
'gif': { 'pdf': 'img-to-pdf' }, 'bmp': { 'pdf': 'img-to-pdf' }, 'tiff': { 'pdf': 'img-to-pdf' }, 'webp': { 'pdf': 'img-to-pdf' },
'html': { 'pdf': 'html-to-pdf' }, 'htm': { 'pdf': 'html-to-pdf' },
'md': { 'pdf': 'markdown-to-pdf' },
- 'txt': { 'pdf': 'file-to-pdf' }, 'rtf': { 'pdf': 'file-to-pdf' }
+ 'txt': { 'pdf': 'file-to-pdf' }, 'rtf': { 'pdf': 'file-to-pdf' },
+ 'eml': { 'pdf': 'eml-to-pdf' }
};
export type ColorType = typeof COLOR_TYPES[keyof typeof COLOR_TYPES];
diff --git a/frontend/src/hooks/tools/convert/useConvertOperation.ts b/frontend/src/hooks/tools/convert/useConvertOperation.ts
index f7b4d20ae..4b639988e 100644
--- a/frontend/src/hooks/tools/convert/useConvertOperation.ts
+++ b/frontend/src/hooks/tools/convert/useConvertOperation.ts
@@ -11,7 +11,7 @@ import {
ENDPOINT_NAMES,
EXTENSION_TO_ENDPOINT
} from '../../../constants/convertConstants';
-import { getEndpointUrl } from '../../../utils/convertUtils';
+import { getEndpointUrl, isImageFormat } from '../../../utils/convertUtils';
export interface ConvertOperationHook {
executeOperation: (
@@ -66,8 +66,8 @@ export const useConvertOperation = (): ConvertOperationHook => {
const { fromExtension, toExtension, imageOptions } = parameters;
// Add conversion-specific parameters
- if (['png', 'jpg'].includes(toExtension)) {
- formData.append("imageFormat", toExtension === 'jpg' ? 'jpg' : 'png');
+ if (isImageFormat(toExtension)) {
+ formData.append("imageFormat", toExtension);
formData.append("colorType", imageOptions.colorType);
formData.append("dpi", imageOptions.dpi.toString());
formData.append("singleOrMultiple", imageOptions.singleOrMultiple);
@@ -77,7 +77,7 @@ export const useConvertOperation = (): ConvertOperationHook => {
formData.append("outputFormat", toExtension);
} else if (fromExtension === 'pdf' && ['txt', 'rtf'].includes(toExtension)) {
formData.append("outputFormat", toExtension);
- } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp'].includes(fromExtension) && toExtension === 'pdf') {
+ } else if (isImageFormat(fromExtension) && toExtension === 'pdf') {
formData.append("fitOption", "fillPage");
formData.append("colorType", imageOptions.colorType);
formData.append("autoRotate", "true");
diff --git a/frontend/src/hooks/useToolManagement.tsx b/frontend/src/hooks/useToolManagement.tsx
index dc8300aec..9f9a62a9e 100644
--- a/frontend/src/hooks/useToolManagement.tsx
+++ b/frontend/src/hooks/useToolManagement.tsx
@@ -33,7 +33,7 @@ const toolEndpoints: Record = {
split: ["split-pages", "split-pdf-by-sections", "split-by-size-or-count", "split-pdf-by-chapters"],
compress: ["compress-pdf"],
merge: ["merge-pdfs"],
- convert: ["pdf-to-img", "img-to-pdf", "pdf-to-word", "pdf-to-presentation", "pdf-to-text", "pdf-to-html", "pdf-to-xml", "html-to-pdf", "markdown-to-pdf", "file-to-pdf"],
+ convert: ["pdf-to-img", "img-to-pdf", "pdf-to-word", "pdf-to-presentation", "pdf-to-text", "pdf-to-csv", "pdf-to-markdown", "pdf-to-html", "pdf-to-xml", "pdf-to-pdfa", "html-to-pdf", "markdown-to-pdf", "file-to-pdf", "eml-to-pdf"],
};
diff --git a/frontend/src/utils/convertUtils.ts b/frontend/src/utils/convertUtils.ts
index deb945a4f..1ba33ef67 100644
--- a/frontend/src/utils/convertUtils.ts
+++ b/frontend/src/utils/convertUtils.ts
@@ -35,4 +35,11 @@ export const getEndpointUrl = (fromExtension: string, toExtension: string): stri
*/
export const isConversionSupported = (fromExtension: string, toExtension: string): boolean => {
return getEndpointName(fromExtension, toExtension) !== '';
+};
+
+/**
+ * Checks if the given extension is an image format
+ */
+export const isImageFormat = (extension: string): boolean => {
+ return ['png', 'jpg', 'jpeg', 'gif', 'tiff', 'bmp', 'webp'].includes(extension.toLowerCase());
};
\ No newline at end of file