mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-17 13:52:14 +01:00
OCR fix and Mobile QR changes (#5433)
# Description of Changes ## OCR / Tesseract path handling Makes tessDataPath resolution deterministic with priority: config > TESSDATA_PREFIX env > default. Updates language discovery to use runtimePathConfig.getTessDataPath() instead of raw config value. Ensure default OCR dir is debian based not alpine ## Mobile scanner: feature gating + new conversion settings Adds system.mobileScannerSettings (convert-to-PDF + resolution + page format + stretch) exposed via backend config and configurable in the proprietary admin UI. Enforces enableMobileScanner on the MobileScannerController endpoints (403 when disabled). Frontend mobile upload flow can now optionally convert received images to PDF (pdf-lib + canvas). ## Desktop/Tauri connectivity work Expands tauri-plugin-http permissions and enables dangerous-settings. Adds a very comprehensive multi-stage server connection diagnostic routine (with lots of logging). <img width="688" height="475" alt="image" src="https://github.com/user-attachments/assets/6f9c1aec-58c7-449b-96b0-52f25430d741" /> --- ## 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) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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.
This commit is contained in:
@@ -191,7 +191,7 @@ public class UIDataController {
|
||||
}
|
||||
|
||||
private List<String> getAvailableTesseractLanguages() {
|
||||
String tessdataDir = applicationProperties.getSystem().getTessdataDir();
|
||||
String tessdataDir = runtimePathConfig.getTessDataPath();
|
||||
java.io.File[] files = new java.io.File(tessdataDir).listFiles();
|
||||
if (files == null) {
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -75,10 +75,25 @@ public class ConfigController {
|
||||
String frontendUrl = applicationProperties.getSystem().getFrontendUrl();
|
||||
configData.put("frontendUrl", frontendUrl != null ? frontendUrl : "");
|
||||
|
||||
// Add mobile scanner setting
|
||||
// Add mobile scanner settings
|
||||
configData.put(
|
||||
"enableMobileScanner",
|
||||
applicationProperties.getSystem().isEnableMobileScanner());
|
||||
configData.put(
|
||||
"mobileScannerConvertToPdf",
|
||||
applicationProperties.getSystem().getMobileScannerSettings().isConvertToPdf());
|
||||
configData.put(
|
||||
"mobileScannerImageResolution",
|
||||
applicationProperties
|
||||
.getSystem()
|
||||
.getMobileScannerSettings()
|
||||
.getImageResolution());
|
||||
configData.put(
|
||||
"mobileScannerPageFormat",
|
||||
applicationProperties.getSystem().getMobileScannerSettings().getPageFormat());
|
||||
configData.put(
|
||||
"mobileScannerStretchToFit",
|
||||
applicationProperties.getSystem().getMobileScannerSettings().isStretchToFit());
|
||||
|
||||
// Extract values from ApplicationProperties
|
||||
configData.put("appNameNavbar", applicationProperties.getUi().getAppNameNavbar());
|
||||
|
||||
@@ -31,6 +31,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.service.MobileScannerService;
|
||||
import stirling.software.common.service.MobileScannerService.FileMetadata;
|
||||
|
||||
@@ -51,9 +52,31 @@ import stirling.software.common.service.MobileScannerService.FileMetadata;
|
||||
public class MobileScannerController {
|
||||
|
||||
private final MobileScannerService mobileScannerService;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public MobileScannerController(MobileScannerService mobileScannerService) {
|
||||
public MobileScannerController(
|
||||
MobileScannerService mobileScannerService,
|
||||
ApplicationProperties applicationProperties) {
|
||||
this.mobileScannerService = mobileScannerService;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if mobile scanner feature is enabled
|
||||
*
|
||||
* @return Error response if disabled, null if enabled
|
||||
*/
|
||||
private ResponseEntity<Map<String, Object>> checkFeatureEnabled() {
|
||||
if (!applicationProperties.getSystem().isEnableMobileScanner()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(
|
||||
Map.of(
|
||||
"error",
|
||||
"Mobile scanner feature is not enabled",
|
||||
"enabled",
|
||||
false));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,10 +94,16 @@ public class MobileScannerController {
|
||||
description = "Session created successfully",
|
||||
content = @Content(schema = @Schema(implementation = SessionInfoResponse.class)))
|
||||
@ApiResponse(responseCode = "400", description = "Invalid session ID")
|
||||
@ApiResponse(responseCode = "403", description = "Mobile scanner feature not enabled")
|
||||
public ResponseEntity<Map<String, Object>> createSession(
|
||||
@Parameter(description = "Session ID for QR code", required = true) @PathVariable
|
||||
String sessionId) {
|
||||
|
||||
ResponseEntity<Map<String, Object>> featureCheck = checkFeatureEnabled();
|
||||
if (featureCheck != null) {
|
||||
return featureCheck;
|
||||
}
|
||||
|
||||
try {
|
||||
MobileScannerService.SessionInfo sessionInfo =
|
||||
mobileScannerService.createSession(sessionId);
|
||||
@@ -109,10 +138,16 @@ public class MobileScannerController {
|
||||
description = "Session is valid",
|
||||
content = @Content(schema = @Schema(implementation = SessionInfoResponse.class)))
|
||||
@ApiResponse(responseCode = "404", description = "Session not found or expired")
|
||||
@ApiResponse(responseCode = "403", description = "Mobile scanner feature not enabled")
|
||||
public ResponseEntity<Map<String, Object>> validateSession(
|
||||
@Parameter(description = "Session ID to validate", required = true) @PathVariable
|
||||
String sessionId) {
|
||||
|
||||
ResponseEntity<Map<String, Object>> featureCheck = checkFeatureEnabled();
|
||||
if (featureCheck != null) {
|
||||
return featureCheck;
|
||||
}
|
||||
|
||||
MobileScannerService.SessionInfo sessionInfo =
|
||||
mobileScannerService.validateSession(sessionId);
|
||||
|
||||
@@ -147,6 +182,7 @@ public class MobileScannerController {
|
||||
description = "Files uploaded successfully",
|
||||
content = @Content(schema = @Schema(implementation = UploadResponse.class)))
|
||||
@ApiResponse(responseCode = "400", description = "Invalid session ID or files")
|
||||
@ApiResponse(responseCode = "403", description = "Mobile scanner feature not enabled")
|
||||
@ApiResponse(responseCode = "500", description = "Upload failed")
|
||||
public ResponseEntity<Map<String, Object>> uploadFiles(
|
||||
@Parameter(description = "Session ID from QR code", required = true) @PathVariable
|
||||
@@ -154,6 +190,11 @@ public class MobileScannerController {
|
||||
@Parameter(description = "Files to upload", required = true) @RequestParam("files")
|
||||
List<MultipartFile> files) {
|
||||
|
||||
ResponseEntity<Map<String, Object>> featureCheck = checkFeatureEnabled();
|
||||
if (featureCheck != null) {
|
||||
return featureCheck;
|
||||
}
|
||||
|
||||
try {
|
||||
if (files == null || files.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", "No files provided"));
|
||||
@@ -194,10 +235,16 @@ public class MobileScannerController {
|
||||
responseCode = "200",
|
||||
description = "File list retrieved",
|
||||
content = @Content(schema = @Schema(implementation = FileListResponse.class)))
|
||||
@ApiResponse(responseCode = "403", description = "Mobile scanner feature not enabled")
|
||||
public ResponseEntity<Map<String, Object>> getSessionFiles(
|
||||
@Parameter(description = "Session ID", required = true) @PathVariable
|
||||
String sessionId) {
|
||||
|
||||
ResponseEntity<Map<String, Object>> featureCheck = checkFeatureEnabled();
|
||||
if (featureCheck != null) {
|
||||
return featureCheck;
|
||||
}
|
||||
|
||||
List<FileMetadata> files = mobileScannerService.getSessionFiles(sessionId);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
@@ -221,12 +268,17 @@ public class MobileScannerController {
|
||||
description =
|
||||
"Download a file that was uploaded to a session. File is automatically deleted after download.")
|
||||
@ApiResponse(responseCode = "200", description = "File downloaded successfully")
|
||||
@ApiResponse(responseCode = "403", description = "Mobile scanner feature not enabled")
|
||||
@ApiResponse(responseCode = "404", description = "File or session not found")
|
||||
public ResponseEntity<Resource> downloadFile(
|
||||
@Parameter(description = "Session ID", required = true) @PathVariable String sessionId,
|
||||
@Parameter(description = "Filename to download", required = true) @PathVariable
|
||||
String filename) {
|
||||
|
||||
if (!applicationProperties.getSystem().isEnableMobileScanner()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
try {
|
||||
Path filePath = mobileScannerService.getFile(sessionId, filename);
|
||||
|
||||
@@ -268,10 +320,16 @@ public class MobileScannerController {
|
||||
summary = "Delete a session",
|
||||
description = "Manually delete a session and all its uploaded files")
|
||||
@ApiResponse(responseCode = "200", description = "Session deleted successfully")
|
||||
@ApiResponse(responseCode = "403", description = "Mobile scanner feature not enabled")
|
||||
public ResponseEntity<Map<String, Object>> deleteSession(
|
||||
@Parameter(description = "Session ID to delete", required = true) @PathVariable
|
||||
String sessionId) {
|
||||
|
||||
ResponseEntity<Map<String, Object>> featureCheck = checkFeatureEnabled();
|
||||
if (featureCheck != null) {
|
||||
return featureCheck;
|
||||
}
|
||||
|
||||
mobileScannerService.deleteSession(sessionId);
|
||||
|
||||
return ResponseEntity.ok(
|
||||
|
||||
@@ -146,6 +146,11 @@ system:
|
||||
backendUrl: '' # Backend base URL for SAML/OAuth/API callbacks (e.g. 'http://localhost:8080' for dev, 'https://api.example.com' for production). REQUIRED for SSO authentication to work correctly. This is where your IdP will send SAML responses and OAuth callbacks. Leave empty to default to 'http://localhost:8080' in development.
|
||||
frontendUrl: '' # Frontend URL for invite email links (e.g. 'https://app.example.com'). Optional - if not set, will use backendUrl. This is the URL users click in invite emails.
|
||||
enableMobileScanner: false # Enable mobile phone QR code upload feature. Requires frontendUrl to be configured.
|
||||
mobileScannerSettings:
|
||||
convertToPdf: true # Automatically convert uploaded images to PDF format. If false, images are kept as-is.
|
||||
imageResolution: full # Image resolution for mobile uploads: 'full' (original size) or 'reduced' (max 1200px on longest side). Only applies when convertToPdf is true.
|
||||
pageFormat: A4 # Page format for converted PDFs: 'keep' (original image dimensions), 'A4' (A4 page size), or 'letter' (US Letter page size). Only applies when convertToPdf is true.
|
||||
stretchToFit: false # Whether to stretch images to fill the entire page (may distort aspect ratio). If false, images are centered with preserved aspect ratio. Only applies when convertToPdf is true.
|
||||
serverCertificate:
|
||||
enabled: true # Enable server-side certificate for "Sign with Stirling-PDF" option
|
||||
organizationName: Stirling-PDF # Organization name for generated certificates
|
||||
|
||||
Reference in New Issue
Block a user