mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 04:09:22 +00:00

# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: Reece Browne <you@example.com>
150 lines
6.4 KiB
TypeScript
150 lines
6.4 KiB
TypeScript
import { useCallback } from 'react';
|
|
import axios from 'axios';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { ConvertParameters } from './useConvertParameters';
|
|
import { detectFileExtension } from '../../../utils/fileUtils';
|
|
import { createFileFromApiResponse } from '../../../utils/fileResponseUtils';
|
|
import { useToolOperation, ToolOperationConfig } from '../shared/useToolOperation';
|
|
import { getEndpointUrl, isImageFormat, isWebFormat } from '../../../utils/convertUtils';
|
|
|
|
const shouldProcessFilesSeparately = (
|
|
selectedFiles: File[],
|
|
parameters: ConvertParameters
|
|
): boolean => {
|
|
return selectedFiles.length > 1 && (
|
|
// Image to PDF with combineImages = false
|
|
((isImageFormat(parameters.fromExtension) || parameters.fromExtension === 'image') &&
|
|
parameters.toExtension === 'pdf' && !parameters.imageOptions.combineImages) ||
|
|
// PDF to image conversions (each PDF should generate its own image file)
|
|
(parameters.fromExtension === 'pdf' && isImageFormat(parameters.toExtension)) ||
|
|
// PDF to PDF/A conversions (each PDF should be processed separately)
|
|
(parameters.fromExtension === 'pdf' && parameters.toExtension === 'pdfa') ||
|
|
// Web files to PDF conversions (each web file should generate its own PDF)
|
|
((isWebFormat(parameters.fromExtension) || parameters.fromExtension === 'web') &&
|
|
parameters.toExtension === 'pdf') ||
|
|
// Web files smart detection
|
|
(parameters.isSmartDetection && parameters.smartDetectionType === 'web') ||
|
|
// Mixed file types (smart detection)
|
|
(parameters.isSmartDetection && parameters.smartDetectionType === 'mixed')
|
|
);
|
|
};
|
|
|
|
const buildFormData = (parameters: ConvertParameters, selectedFiles: File[]): FormData => {
|
|
const formData = new FormData();
|
|
|
|
selectedFiles.forEach(file => {
|
|
formData.append("fileInput", file);
|
|
});
|
|
|
|
const { fromExtension, toExtension, imageOptions, htmlOptions, emailOptions, pdfaOptions } = parameters;
|
|
|
|
if (isImageFormat(toExtension)) {
|
|
formData.append("imageFormat", toExtension);
|
|
formData.append("colorType", imageOptions.colorType);
|
|
formData.append("dpi", imageOptions.dpi.toString());
|
|
formData.append("singleOrMultiple", imageOptions.singleOrMultiple);
|
|
} else if (fromExtension === 'pdf' && ['docx', 'odt'].includes(toExtension)) {
|
|
formData.append("outputFormat", toExtension);
|
|
} else if (fromExtension === 'pdf' && ['pptx', 'odp'].includes(toExtension)) {
|
|
formData.append("outputFormat", toExtension);
|
|
} else if (fromExtension === 'pdf' && ['txt', 'rtf'].includes(toExtension)) {
|
|
formData.append("outputFormat", toExtension);
|
|
} else if ((isImageFormat(fromExtension) || fromExtension === 'image') && toExtension === 'pdf') {
|
|
formData.append("fitOption", imageOptions.fitOption);
|
|
formData.append("colorType", imageOptions.colorType);
|
|
formData.append("autoRotate", imageOptions.autoRotate.toString());
|
|
} else if ((fromExtension === 'html' || fromExtension === 'zip') && toExtension === 'pdf') {
|
|
formData.append("zoom", htmlOptions.zoomLevel.toString());
|
|
} else if (fromExtension === 'eml' && toExtension === 'pdf') {
|
|
formData.append("includeAttachments", emailOptions.includeAttachments.toString());
|
|
formData.append("maxAttachmentSizeMB", emailOptions.maxAttachmentSizeMB.toString());
|
|
formData.append("downloadHtml", emailOptions.downloadHtml.toString());
|
|
formData.append("includeAllRecipients", emailOptions.includeAllRecipients.toString());
|
|
} else if (fromExtension === 'pdf' && toExtension === 'pdfa') {
|
|
formData.append("outputFormat", pdfaOptions.outputFormat);
|
|
} else if (fromExtension === 'pdf' && toExtension === 'csv') {
|
|
formData.append("pageNumbers", "all");
|
|
}
|
|
|
|
return formData;
|
|
};
|
|
|
|
const createFileFromResponse = (
|
|
responseData: any,
|
|
headers: any,
|
|
originalFileName: string,
|
|
targetExtension: string
|
|
): File => {
|
|
const originalName = originalFileName.split('.')[0];
|
|
const fallbackFilename = `${originalName}_converted.${targetExtension}`;
|
|
|
|
return createFileFromApiResponse(responseData, headers, fallbackFilename);
|
|
};
|
|
|
|
export const useConvertOperation = () => {
|
|
const { t } = useTranslation();
|
|
|
|
const customConvertProcessor = useCallback(async (
|
|
parameters: ConvertParameters,
|
|
selectedFiles: File[]
|
|
): Promise<File[]> => {
|
|
|
|
const processedFiles: File[] = [];
|
|
const endpoint = getEndpointUrl(parameters.fromExtension, parameters.toExtension);
|
|
|
|
if (!endpoint) {
|
|
throw new Error(t('errorNotSupported', 'Unsupported conversion format'));
|
|
}
|
|
|
|
// Convert-specific routing logic: decide batch vs individual processing
|
|
if (shouldProcessFilesSeparately(selectedFiles, parameters)) {
|
|
// Individual processing for complex cases (PDF→image, smart detection, etc.)
|
|
for (const file of selectedFiles) {
|
|
try {
|
|
const formData = buildFormData(parameters, [file]);
|
|
const response = await axios.post(endpoint, formData, { responseType: 'blob' });
|
|
|
|
const convertedFile = createFileFromResponse(response.data, response.headers, file.name, parameters.toExtension);
|
|
|
|
processedFiles.push(convertedFile);
|
|
} catch (error) {
|
|
console.warn(`Failed to convert file ${file.name}:`, error);
|
|
}
|
|
}
|
|
} else {
|
|
// Batch processing for simple cases (image→PDF combine)
|
|
const formData = buildFormData(parameters, selectedFiles);
|
|
const response = await axios.post(endpoint, formData, { responseType: 'blob' });
|
|
|
|
const baseFilename = selectedFiles.length === 1
|
|
? selectedFiles[0].name
|
|
: 'converted_files';
|
|
|
|
const convertedFile = createFileFromResponse(response.data, response.headers, baseFilename, parameters.toExtension);
|
|
processedFiles.push(convertedFile);
|
|
|
|
}
|
|
|
|
return processedFiles;
|
|
}, [t]);
|
|
|
|
return useToolOperation<ConvertParameters>({
|
|
operationType: 'convert',
|
|
endpoint: '', // Not used with customProcessor but required
|
|
buildFormData, // Not used with customProcessor but required
|
|
filePrefix: 'converted_',
|
|
customProcessor: customConvertProcessor, // Convert handles its own routing
|
|
validateParams: (params) => {
|
|
return { valid: true };
|
|
},
|
|
getErrorMessage: (error) => {
|
|
if (error.response?.data && typeof error.response.data === 'string') {
|
|
return error.response.data;
|
|
}
|
|
if (error.message) {
|
|
return error.message;
|
|
}
|
|
return t("convert.errorConversion", "An error occurred while converting the file.");
|
|
}
|
|
});
|
|
}; |