More conversions

This commit is contained in:
Connor Yoh 2025-07-25 11:36:57 +01:00
parent da3a0068fe
commit 73edf3f08c
5 changed files with 71 additions and 21 deletions

View File

@ -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 = ({
</Stack>
{/* Format-specific options */}
{['png', 'jpg'].includes(parameters.toExtension) && (
{isImageFormat(parameters.toExtension) && (
<>
<Divider />
<ConvertToImageSettings
@ -162,7 +172,7 @@ const ConvertSettings = ({
{/* Color options for image to PDF conversion */}
{['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp'].includes(parameters.fromExtension) && parameters.toExtension === 'pdf' && (
{isImageFormat(parameters.fromExtension) && parameters.toExtension === 'pdf' && (
<>
<Divider />
<ConvertFromImageSettings
@ -172,6 +182,19 @@ const ConvertSettings = ({
/>
</>
)}
{/* EML specific options */}
{parameters.fromExtension === 'eml' && parameters.toExtension === 'pdf' && (
<>
<Divider />
<Stack gap="sm">
<Text size="sm" fw={500}>{t("convert.emlOptions", "Email Options")}:</Text>
<Text size="xs" c="dimmed">
{t("convert.emlNote", "Email attachments and embedded images will be included in the PDF conversion.")}
</Text>
</Stack>
</>
)}
</Stack>
);
};

View File

@ -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<string, string[]> = {
'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<string, Record<string, string>> = {
'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<string, Record<string, string>> = {
'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];

View File

@ -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");

View File

@ -33,7 +33,7 @@ const toolEndpoints: Record<string, string[]> = {
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"],
};

View File

@ -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());
};