mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 18:44:05 +02:00
# Description of Changes This pull request introduces enhancements to tool dependencies, endpoint configurations, and Dockerfile packages. The changes primarily focus on adding support for new tools (Ghostscript and OCRmyPDF), improving endpoint management, and refining dependency checks. Below is a summary of the most important changes grouped by theme: ### Tool Integration and Dependency Management: * Added support for Ghostscript and OCRmyPDF in the session limits and timeout configurations (`ApplicationProperties.java`). [[1]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R548-R549) [[2]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R582-R589) [[3]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R602-R603) [[4]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R636-R643) * Updated `Processes` enum and `ProcessExecutor` logic to include Ghostscript and OCRmyPDF, enabling their session limits and timeout handling. [[1]](diffhunk://#diff-b0afb37bdac8b0f1a10aca87b0244b133ac9b05518088b6f4c6c2bf48f859fdaR87-R96) [[2]](diffhunk://#diff-b0afb37bdac8b0f1a10aca87b0244b133ac9b05518088b6f4c6c2bf48f859fdaR141-R150) [[3]](diffhunk://#diff-b0afb37bdac8b0f1a10aca87b0244b133ac9b05518088b6f4c6c2bf48f859fdaL281-R303) * Integrated Ghostscript and OCRmyPDF into the external dependency check mechanism (`ExternalAppDepConfig.java`). [[1]](diffhunk://#diff-511713d04e0545670bfb44e70e84235288e91db0ef219e423628746f28e67cfcR37-R38) [[2]](diffhunk://#diff-511713d04e0545670bfb44e70e84235288e91db0ef219e423628746f28e67cfcR114-R115) ### Endpoint Configuration Enhancements: * Improved endpoint management by introducing functional group overrides, tool group fallbacks, and alternative tool group handling (`EndpointConfiguration.java`). [[1]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dR24-R25) [[2]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dR39-R178) [[3]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dL264-R357) [[4]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dR399-R437) * Added new endpoint groups and alternatives for multi-tool endpoints like "repair," "compress-pdf," and "ocr-pdf," supporting both Ghostscript and OCRmyPDF. [[1]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dL215-L216) [[2]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dL264-R357) ### Dockerfile Updates: * Removed `qpdf` and added `unpaper` and `ocrmypdf` to the package list in `Dockerfile` and `Dockerfile.fat` to enable advanced OCR and PDF manipulation features. [[1]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557L54) [[2]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R71-R75) [[3]](diffhunk://#diff-571631582b988e88c52c86960cc083b0b8fa63cf88f056f26e9e684195221c27L79-R89) ### Code Cleanup and Refactoring: * Refactored `TempFileRegistry` to improve readability by simplifying variable initialization. [[1]](diffhunk://#diff-ed08845255ade38ea6e28a959ed967957611025fb7f8f5dcf4543b5f200a5fecL12) [[2]](diffhunk://#diff-ed08845255ade38ea6e28a959ed967957611025fb7f8f5dcf4543b5f200a5fecL30-R29) * Updated `CompressController` to dynamically check tool group availability using the new endpoint configuration logic. [[1]](diffhunk://#diff-fc8dc1845d34077a089d9265521ce8c8d6104a8b79137e1de5310e30ffb0348aR50) [[2]](diffhunk://#diff-fc8dc1845d34077a089d9265521ce8c8d6104a8b79137e1de5310e30ffb0348aR65-R76) These changes collectively enhance the application's capabilities for PDF processing, improve dependency management, and streamline endpoint handling. --- ## 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/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/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/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/DeveloperGuide.md#6-testing) for more details.
163 lines
6.6 KiB
Java
163 lines
6.6 KiB
Java
package stirling.software.SPDF.config;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.stream.Collectors;
|
|
|
|
import org.springframework.context.annotation.Configuration;
|
|
|
|
import jakarta.annotation.PostConstruct;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import stirling.software.common.configuration.RuntimePathConfig;
|
|
|
|
@Configuration
|
|
@Slf4j
|
|
public class ExternalAppDepConfig {
|
|
|
|
private final EndpointConfiguration endpointConfiguration;
|
|
|
|
private final String weasyprintPath;
|
|
private final String unoconvPath;
|
|
private final Map<String, List<String>> commandToGroupMapping;
|
|
|
|
public ExternalAppDepConfig(
|
|
EndpointConfiguration endpointConfiguration, RuntimePathConfig runtimePathConfig) {
|
|
this.endpointConfiguration = endpointConfiguration;
|
|
weasyprintPath = runtimePathConfig.getWeasyPrintPath();
|
|
unoconvPath = runtimePathConfig.getUnoConvertPath();
|
|
|
|
commandToGroupMapping =
|
|
new HashMap<>() {
|
|
|
|
{
|
|
put("gs", List.of("Ghostscript"));
|
|
put("ocrmypdf", List.of("OCRmyPDF"));
|
|
put("soffice", List.of("LibreOffice"));
|
|
put(weasyprintPath, List.of("Weasyprint"));
|
|
put("pdftohtml", List.of("Pdftohtml"));
|
|
put(unoconvPath, List.of("Unoconvert"));
|
|
put("qpdf", List.of("qpdf"));
|
|
put("tesseract", List.of("tesseract"));
|
|
}
|
|
};
|
|
}
|
|
|
|
private boolean isCommandAvailable(String command) {
|
|
try {
|
|
ProcessBuilder processBuilder = new ProcessBuilder();
|
|
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
|
processBuilder.command("where", command);
|
|
} else {
|
|
processBuilder.command("which", command);
|
|
}
|
|
Process process = processBuilder.start();
|
|
int exitCode = process.waitFor();
|
|
return exitCode == 0;
|
|
} catch (Exception e) {
|
|
log.debug("Error checking for command {}: {}", command, e.getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private List<String> getAffectedFeatures(String group) {
|
|
return endpointConfiguration.getEndpointsForGroup(group).stream()
|
|
.map(endpoint -> formatEndpointAsFeature(endpoint))
|
|
.toList();
|
|
}
|
|
|
|
private String formatEndpointAsFeature(String endpoint) {
|
|
// First replace common terms
|
|
String feature = endpoint.replace("-", " ").replace("pdf", "PDF").replace("img", "image");
|
|
// Split into words and capitalize each word
|
|
return Arrays.stream(feature.split("\\s+"))
|
|
.map(word -> capitalizeWord(word))
|
|
.collect(Collectors.joining(" "));
|
|
}
|
|
|
|
private String capitalizeWord(String word) {
|
|
if (word.isEmpty()) {
|
|
return word;
|
|
}
|
|
if ("pdf".equalsIgnoreCase(word)) {
|
|
return "PDF";
|
|
}
|
|
return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
|
|
}
|
|
|
|
private void checkDependencyAndDisableGroup(String command) {
|
|
boolean isAvailable = isCommandAvailable(command);
|
|
if (!isAvailable) {
|
|
List<String> affectedGroups = commandToGroupMapping.get(command);
|
|
if (affectedGroups != null) {
|
|
for (String group : affectedGroups) {
|
|
List<String> affectedFeatures = getAffectedFeatures(group);
|
|
endpointConfiguration.disableGroup(group);
|
|
log.warn(
|
|
"Missing dependency: {} - Disabling group: {} (Affected features: {})",
|
|
command,
|
|
group,
|
|
affectedFeatures != null && !affectedFeatures.isEmpty()
|
|
? String.join(", ", affectedFeatures)
|
|
: "unknown");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@PostConstruct
|
|
public void checkDependencies() {
|
|
// Check core dependencies
|
|
checkDependencyAndDisableGroup("gs");
|
|
checkDependencyAndDisableGroup("ocrmypdf");
|
|
checkDependencyAndDisableGroup("tesseract");
|
|
checkDependencyAndDisableGroup("soffice");
|
|
checkDependencyAndDisableGroup("qpdf");
|
|
checkDependencyAndDisableGroup(weasyprintPath);
|
|
checkDependencyAndDisableGroup("pdftohtml");
|
|
checkDependencyAndDisableGroup(unoconvPath);
|
|
// Special handling for Python/OpenCV dependencies
|
|
boolean pythonAvailable = isCommandAvailable("python3") || isCommandAvailable("python");
|
|
if (!pythonAvailable) {
|
|
List<String> pythonFeatures = getAffectedFeatures("Python");
|
|
List<String> openCVFeatures = getAffectedFeatures("OpenCV");
|
|
endpointConfiguration.disableGroup("Python");
|
|
endpointConfiguration.disableGroup("OpenCV");
|
|
log.warn(
|
|
"Missing dependency: Python - Disabling Python features: {} and OpenCV features: {}",
|
|
String.join(", ", pythonFeatures),
|
|
String.join(", ", openCVFeatures));
|
|
} else {
|
|
// If Python is available, check for OpenCV
|
|
try {
|
|
ProcessBuilder processBuilder = new ProcessBuilder();
|
|
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
|
processBuilder.command("python", "-c", "import cv2");
|
|
} else {
|
|
processBuilder.command("python3", "-c", "import cv2");
|
|
}
|
|
Process process = processBuilder.start();
|
|
int exitCode = process.waitFor();
|
|
if (exitCode != 0) {
|
|
List<String> openCVFeatures = getAffectedFeatures("OpenCV");
|
|
endpointConfiguration.disableGroup("OpenCV");
|
|
log.warn(
|
|
"OpenCV not available in Python - Disabling OpenCV features: {}",
|
|
String.join(", ", openCVFeatures));
|
|
}
|
|
} catch (Exception e) {
|
|
List<String> openCVFeatures = getAffectedFeatures("OpenCV");
|
|
endpointConfiguration.disableGroup("OpenCV");
|
|
log.warn(
|
|
"Error checking OpenCV: {} - Disabling OpenCV features: {}",
|
|
e.getMessage(),
|
|
String.join(", ", openCVFeatures));
|
|
}
|
|
}
|
|
endpointConfiguration.logDisabledEndpointsSummary();
|
|
}
|
|
}
|