Compare commits

...

10 Commits

Author SHA1 Message Date
Anthony Stirling
bc2b15cbae Merge branch 'main' into bugFixes 2025-02-07 13:11:58 +00:00
Anthony Stirling
09c1ee0c34 formatting 2025-02-07 13:10:11 +00:00
thiagoor-cpu
242aa5eae1 Update messages_pt_BR.properties (#2905)
Up-to-date 0.40.2

# Description of Changes

Up-to-date 0.40.2

Closes #(issue_number)

---

## Checklist

### General

- [X] 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)
- [X] 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.
2025-02-07 13:08:31 +00:00
Anthony Stirling
72ee00a264 pipeline bug, doc bugs, auto split new URL and doc 2025-02-07 13:08:12 +00:00
a
c62008a183 Merge remote-tracking branch 'origin/main' into pdfafixes 2025-02-07 13:06:57 +00:00
Anthony Stirling
bf65c456d1 PDFA fixes (#2896)
# 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/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.
2025-02-07 13:06:19 +00:00
Anthony Stirling
e0c83adc9f fix docker merge 2025-02-07 12:55:39 +00:00
reecebrowne
26720c5018 Fix for tab issue (#2898)
# 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/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.
2025-02-06 17:08:33 +00:00
Anthony Stirling
1eee6ee356 use libreoffice for pdfa 2025-02-06 15:55:22 +00:00
reecebrowne
2ed07e3fcb Homepage hotfix (#2894)
# Description of Changes

Fix replace-and-invert-color-pdf nav
Fix legacy homepage search
Closes #(2888)

---

## Checklist

### General

- [ x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x ] 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.

---------

Co-authored-by: Reece Browne <reece@stirling.pdf>
2025-02-06 10:38:14 +00:00
25 changed files with 246 additions and 154 deletions

View File

@@ -56,13 +56,15 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
openssl-dev \ openssl-dev \
openjdk21-jre \ openjdk21-jre \
# Doc conversion # Doc conversion
gcompat \
libc6-compat \
libreoffice \ libreoffice \
# pdftohtml # pdftohtml
poppler-utils \ poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced features) # OCR MY PDF (unpaper for descew and other advanced features)
tesseract-ocr-data-eng \ tesseract-ocr-data-eng \
# CV # CV
py3-opencv \ py3-opencv \
# python3/pip # python3/pip
python3 \ python3 \
py3-pip && \ py3-pip && \

View File

@@ -57,6 +57,8 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
openssl-dev \ openssl-dev \
openjdk21-jre \ openjdk21-jre \
# Doc conversion # Doc conversion
gcompat \
libc6-compat \
libreoffice \ libreoffice \
# pdftohtml # pdftohtml
poppler-utils \ poppler-utils \
@@ -65,7 +67,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
tesseract-ocr-data-eng \ tesseract-ocr-data-eng \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \ font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \
# CV # CV
py3-opencv \ py3-opencv \
# python3/pip # python3/pip
python3 \ python3 \
py3-pip && \ py3-pip && \

View File

@@ -26,7 +26,7 @@ ext {
} }
group = "stirling.software" group = "stirling.software"
version = "0.40.2" version = "0.41.0"
java { java {
// 17 is lowest but we support and recommend 21 // 17 is lowest but we support and recommend 21

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -265,9 +265,6 @@ public class EndpointConfiguration {
// Pdftohtml dependent endpoints // Pdftohtml dependent endpoints
addEndpointToGroup("Pdftohtml", "pdf-to-html"); addEndpointToGroup("Pdftohtml", "pdf-to-html");
addEndpointToGroup("Pdftohtml", "pdf-to-markdown"); addEndpointToGroup("Pdftohtml", "pdf-to-markdown");
// disabled for now while we resolve issues
disableEndpoint("pdf-to-pdfa");
} }
private void processEnvironmentConfigs() { private void processEnvironmentConfigs() {

View File

@@ -73,8 +73,8 @@ public class ConvertPDFToPDFA {
// Determine PDF/A filter based on requested format // Determine PDF/A filter based on requested format
String pdfFilter = String pdfFilter =
"pdfa".equals(outputFormat) "pdfa".equals(outputFormat)
? "writer_pdf_Export:{'SelectPdfVersion':{'Value':'2'}}:writer_pdf_Export" ? "pdf:writer_pdf_Export:{\"SelectPdfVersion\":{\"type\":\"long\",\"value\":\"2\"}}"
: "writer_pdf_Export:{'SelectPdfVersion':{'Value':'1'}}:writer_pdf_Export"; : "pdf:writer_pdf_Export:{\"SelectPdfVersion\":{\"type\":\"long\",\"value\":\"1\"}}";
// Prepare LibreOffice command // Prepare LibreOffice command
List<String> command = List<String> command =
@@ -84,7 +84,7 @@ public class ConvertPDFToPDFA {
"--headless", "--headless",
"--nologo", "--nologo",
"--convert-to", "--convert-to",
"pdf:" + pdfFilter, pdfFilter,
"--outdir", "--outdir",
tempOutputDir.toString(), tempOutputDir.toString(),
tempInputFile.toString())); tempInputFile.toString()));

View File

@@ -8,7 +8,9 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@@ -41,8 +43,12 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Misc", description = "Miscellaneous APIs") @Tag(name = "Misc", description = "Miscellaneous APIs")
public class AutoSplitPdfController { public class AutoSplitPdfController {
private static final String QR_CONTENT = "https://github.com/Stirling-Tools/Stirling-PDF"; private static final Set<String> VALID_QR_CONTENTS =
private static final String QR_CONTENT_OLD = "https://github.com/Frooodle/Stirling-PDF"; new HashSet<>(
Set.of(
"https://github.com/Stirling-Tools/Stirling-PDF",
"https://github.com/Frooodle/Stirling-PDF",
"https://stirlingpdf.com"));
private final CustomPDDocumentFactory pdfDocumentFactory; private final CustomPDDocumentFactory pdfDocumentFactory;
@@ -120,13 +126,14 @@ public class AutoSplitPdfController {
for (int page = 0; page < document.getNumberOfPages(); ++page) { for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150); BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150);
String result = decodeQRCode(bim); String result = decodeQRCode(bim);
if ((QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result)) && page != 0) {
boolean isValidQrCode = VALID_QR_CONTENTS.contains(result);
log.debug("detected qr code {}, code is vale={}", result, isValidQrCode);
if (isValidQrCode && page != 0) {
splitDocuments.add(new PDDocument()); splitDocuments.add(new PDDocument());
} }
if (!splitDocuments.isEmpty() if (!splitDocuments.isEmpty() && !isValidQrCode) {
&& !QR_CONTENT.equals(result)
&& !QR_CONTENT_OLD.equals(result)) {
splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page)); splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page));
} else if (page == 0) { } else if (page == 0) {
PDDocument firstDocument = new PDDocument(); PDDocument firstDocument = new PDDocument();
@@ -135,7 +142,7 @@ public class AutoSplitPdfController {
} }
// If duplexMode is true and current page is a divider, then skip next page // If duplexMode is true and current page is a divider, then skip next page
if (duplexMode && (QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result))) { if (duplexMode && isValidQrCode) {
page++; page++;
} }
} }
@@ -168,6 +175,9 @@ public class AutoSplitPdfController {
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} catch (Exception e) {
log.error("Error in auto split", e);
throw e;
} finally { } finally {
// Clean up resources // Clean up resources
if (document != null) { if (document != null) {

View File

@@ -52,7 +52,7 @@ public class ExtractImagesController {
@Operation( @Operation(
summary = "Extract images from a PDF file", summary = "Extract images from a PDF file",
description = description =
"This endpoint extracts images from a given PDF file and returns them in a zip file. Users can specify the output image format. Input: PDF Output: IMAGE/ZIP Type: SIMO") "This endpoint extracts images from a given PDF file and returns them in a zip file. Users can specify the output image format. Input:PDF Output:IMAGE/ZIP Type:SIMO")
public ResponseEntity<byte[]> extractImages(@ModelAttribute PDFExtractImagesRequest request) public ResponseEntity<byte[]> extractImages(@ModelAttribute PDFExtractImagesRequest request)
throws IOException, InterruptedException, ExecutionException { throws IOException, InterruptedException, ExecutionException {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();

View File

@@ -46,7 +46,7 @@ public class FlattenController {
@Operation( @Operation(
summary = "Flatten PDF form fields or full page", summary = "Flatten PDF form fields or full page",
description = description =
"Flattening just PDF form fields or converting each page to images to make text unselectable. Input: PDF, Output: PDF. Type: SISO") "Flattening just PDF form fields or converting each page to images to make text unselectable. Input:PDF, Output:PDF. Type:SISO")
public ResponseEntity<byte[]> flatten(@ModelAttribute FlattenRequest request) throws Exception { public ResponseEntity<byte[]> flatten(@ModelAttribute FlattenRequest request) throws Exception {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();

View File

@@ -8,7 +8,7 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import io.swagger.v3.oas.annotations.Operation;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.pdfbox.multipdf.PDFMergerUtility; import org.apache.pdfbox.multipdf.PDFMergerUtility;
@@ -26,6 +26,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.BoundedLineReader; import io.github.pixee.security.BoundedLineReader;
import io.github.pixee.security.Filenames; import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -65,9 +66,10 @@ public class OCRController {
} }
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf") @PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
@Operation( @Operation(
summary = "Process PDF files with OCR using Tesseract", summary = "Process PDF files with OCR using Tesseract",
description = "Takes a PDF file as input, performs OCR using specified languages and OCR type (skip-text/force-ocr), and returns the processed PDF. Input:PDF Output:PDF Type:SISO") description =
"Takes a PDF file as input, performs OCR using specified languages and OCR type (skip-text/force-ocr), and returns the processed PDF. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> processPdfWithOCR( public ResponseEntity<byte[]> processPdfWithOCR(
@ModelAttribute ProcessPdfWithOcrRequest request) @ModelAttribute ProcessPdfWithOcrRequest request)
throws IOException, InterruptedException { throws IOException, InterruptedException {

View File

@@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.PipelineConfig; import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineResult;
import stirling.software.SPDF.model.api.HandleDataRequest; import stirling.software.SPDF.model.api.HandleDataRequest;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -58,7 +59,8 @@ public class PipelineController {
if (inputFiles == null || inputFiles.size() == 0) { if (inputFiles == null || inputFiles.size() == 0) {
return null; return null;
} }
List<Resource> outputFiles = processor.runPipelineAgainstFiles(inputFiles, config); PipelineResult result = processor.runPipelineAgainstFiles(inputFiles, config);
List<Resource> outputFiles = result.getOutputFiles();
if (outputFiles != null && outputFiles.size() == 1) { if (outputFiles != null && outputFiles.size() == 1) {
// If there is only one file, return it directly // If there is only one file, return it directly
Resource singleFile = outputFiles.get(0); Resource singleFile = outputFiles.get(0);

View File

@@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig; import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.PipelineConfig; import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation; import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.model.PipelineResult;
import stirling.software.SPDF.utils.FileMonitor; import stirling.software.SPDF.utils.FileMonitor;
@Service @Service
@@ -143,19 +144,64 @@ public class PipelineDirectoryProcessor {
private File[] collectFilesForProcessing(Path dir, Path jsonFile, PipelineOperation operation) private File[] collectFilesForProcessing(Path dir, Path jsonFile, PipelineOperation operation)
throws IOException { throws IOException {
List<String> inputExtensions =
apiDocService.getExtensionTypes(false, operation.getOperation());
log.info(
"Allowed extensions for operation {}: {}",
operation.getOperation(),
inputExtensions);
boolean allowAllFiles = inputExtensions.contains("ALL");
try (Stream<Path> paths = Files.list(dir)) { try (Stream<Path> paths = Files.list(dir)) {
if ("automated".equals(operation.getParameters().get("fileInput"))) { File[] files =
return paths.filter( paths.filter(
path -> path -> {
!Files.isDirectory(path) if (Files.isDirectory(path)) {
&& !path.equals(jsonFile) return false;
&& fileMonitor.isFileReadyForProcessing(path)) }
.map(Path::toFile) if (path.equals(jsonFile)) {
.toArray(File[]::new); return false;
} else { }
String fileInput = (String) operation.getParameters().get("fileInput");
return new File[] {new File(fileInput)}; // Get file extension
} String filename = path.getFileName().toString();
String extension =
filename.contains(".")
? filename.substring(
filename.lastIndexOf(".")
+ 1)
.toLowerCase()
: "";
// Check against allowed extensions
boolean isAllowed =
allowAllFiles
|| inputExtensions.contains(extension);
if (!isAllowed) {
log.info(
"Skipping file with unsupported extension: {} ({})",
filename,
extension);
}
return isAllowed;
})
.filter(
path -> {
boolean isReady =
fileMonitor.isFileReadyForProcessing(path);
if (!isReady) {
log.info(
"File not ready for processing (locked/created last 5s): {}",
path);
}
return isReady;
})
.map(Path::toFile)
.toArray(File[]::new);
log.info("Collected {} files for processing", files.length);
return files;
} }
} }
@@ -198,19 +244,37 @@ public class PipelineDirectoryProcessor {
try { try {
List<Resource> inputFiles = List<Resource> inputFiles =
processor.generateInputFiles(filesToProcess.toArray(new File[0])); processor.generateInputFiles(filesToProcess.toArray(new File[0]));
if (inputFiles == null || inputFiles.size() == 0) { if (inputFiles == null || inputFiles.isEmpty()) {
return; return;
} }
List<Resource> outputFiles = processor.runPipelineAgainstFiles(inputFiles, config); PipelineResult result = processor.runPipelineAgainstFiles(inputFiles, config);
if (outputFiles == null) return;
moveAndRenameFiles(outputFiles, config, dir); if (result.isHasErrors()) {
deleteOriginalFiles(filesToProcess, processingDir); log.error("Errors occurred during processing, retaining original files");
moveToErrorDirectory(filesToProcess, dir);
} else {
moveAndRenameFiles(result.getOutputFiles(), config, dir);
deleteOriginalFiles(filesToProcess, processingDir);
}
} catch (Exception e) { } catch (Exception e) {
log.error("error during processing", e); log.error("Error during processing", e);
moveFilesBack(filesToProcess, processingDir); moveFilesBack(filesToProcess, processingDir);
} }
} }
private void moveToErrorDirectory(List<File> files, Path originalDir) throws IOException {
Path errorDir = originalDir.resolve("error");
if (!Files.exists(errorDir)) {
Files.createDirectories(errorDir);
}
for (File file : files) {
Path target = errorDir.resolve(file.getName());
Files.move(file.toPath(), target);
log.info("Moved failed file to error directory for investigation: {}", target);
}
}
private void moveAndRenameFiles(List<Resource> resources, PipelineConfig config, Path dir) private void moveAndRenameFiles(List<Resource> resources, PipelineConfig config, Path dir)
throws IOException { throws IOException {
for (Resource resource : resources) { for (Resource resource : resources) {

View File

@@ -33,6 +33,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.SPDFApplication; import stirling.software.SPDF.SPDFApplication;
import stirling.software.SPDF.model.PipelineConfig; import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation; import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.model.PipelineResult;
import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.Role;
@Service @Service
@@ -84,8 +85,10 @@ public class PipelineProcessor {
return "http://localhost:" + port + contextPath + "/"; return "http://localhost:" + port + contextPath + "/";
} }
List<Resource> runPipelineAgainstFiles(List<Resource> outputFiles, PipelineConfig config) PipelineResult runPipelineAgainstFiles(List<Resource> outputFiles, PipelineConfig config)
throws Exception { throws Exception {
PipelineResult result = new PipelineResult();
ByteArrayOutputStream logStream = new ByteArrayOutputStream(); ByteArrayOutputStream logStream = new ByteArrayOutputStream();
PrintStream logPrintStream = new PrintStream(logStream); PrintStream logPrintStream = new PrintStream(logStream);
boolean hasErrors = false; boolean hasErrors = false;
@@ -130,7 +133,8 @@ public class PipelineProcessor {
if (operation.startsWith("filter-") if (operation.startsWith("filter-")
&& (response.getBody() == null && (response.getBody() == null
|| response.getBody().length == 0)) { || response.getBody().length == 0)) {
log.info("Skipping file due to failing {}", operation); result.setFiltersApplied(true);
log.info("Skipping file due to filtering {}", operation);
continue; continue;
} }
if (!response.getStatusCode().equals(HttpStatus.OK)) { if (!response.getStatusCode().equals(HttpStatus.OK)) {
@@ -208,7 +212,10 @@ public class PipelineProcessor {
if (hasErrors) { if (hasErrors) {
log.error("Errors occurred during processing. Log: {}", logStream.toString()); log.error("Errors occurred during processing. Log: {}", logStream.toString());
} }
return outputFiles; result.setHasErrors(hasErrors);
result.setFiltersApplied(hasErrors);
result.setOutputFiles(outputFiles);
return result;
} }
private ResponseEntity<byte[]> sendWebRequest(String url, MultiValueMap<String, Object> body) { private ResponseEntity<byte[]> sendWebRequest(String url, MultiValueMap<String, Object> body) {

View File

@@ -40,8 +40,7 @@ public class RemoveCertSignController {
@Operation( @Operation(
summary = "Remove digital signature from PDF", summary = "Remove digital signature from PDF",
description = description =
"This endpoint accepts a PDF file and returns the PDF file without the digital signature." "This endpoint accepts a PDF file and returns the PDF file without the digital signature. Input:PDF, Output:PDF Type:SISO")
+ " Input: PDF, Output: PDF")
public ResponseEntity<byte[]> removeCertSignPDF(@ModelAttribute PDFFile request) public ResponseEntity<byte[]> removeCertSignPDF(@ModelAttribute PDFFile request)
throws Exception { throws Exception {
MultipartFile pdf = request.getFileInput(); MultipartFile pdf = request.getFileInput();

View File

@@ -0,0 +1,14 @@
package stirling.software.SPDF.model;
import java.util.List;
import org.springframework.core.io.Resource;
import lombok.Data;
@Data
public class PipelineResult {
private List<Resource> outputFiles;
private boolean hasErrors;
private boolean filtersApplied;
}

View File

@@ -768,7 +768,6 @@ autoSplitPDF.selectText.3=Upload the single large scanned PDF file and let Stirl
autoSplitPDF.selectText.4=Divider pages are automatically detected and removed, guaranteeing a neat final document. autoSplitPDF.selectText.4=Divider pages are automatically detected and removed, guaranteeing a neat final document.
autoSplitPDF.formPrompt=Submit PDF containing Stirling-PDF Page dividers: autoSplitPDF.formPrompt=Submit PDF containing Stirling-PDF Page dividers:
autoSplitPDF.duplexMode=Duplex Mode (Front and back scanning) autoSplitPDF.duplexMode=Duplex Mode (Front and back scanning)
autoSplitPDF.dividerDownload1=Download 'Auto Splitter Divider (minimal).pdf'
autoSplitPDF.dividerDownload2=Download 'Auto Splitter Divider (with instructions).pdf' autoSplitPDF.dividerDownload2=Download 'Auto Splitter Divider (with instructions).pdf'
autoSplitPDF.submit=Submit autoSplitPDF.submit=Submit
@@ -1185,7 +1184,7 @@ changeMetadata.submit=Change
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF To PDF/A pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=PDF To PDF/A pdfToPDFA.header=PDF To PDF/A
pdfToPDFA.credit=This service uses qpdf for PDF/A conversion pdfToPDFA.credit=This service uses libreoffice for PDF/A conversion
pdfToPDFA.submit=Convert pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format

View File

@@ -1185,7 +1185,7 @@ changeMetadata.submit=Change
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF To PDF/A pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=PDF To PDF/A pdfToPDFA.header=PDF To PDF/A
pdfToPDFA.credit=This service uses qpdf for PDF/A conversion pdfToPDFA.credit=This service uses libreoffice for PDF/A conversion
pdfToPDFA.submit=Convert pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format

View File

@@ -138,7 +138,7 @@ analytics.settings=Você pode alterar as configurações de coleta de dados no a
# NAVBAR # # NAVBAR #
############# #############
navbar.favorite=Favoritos navbar.favorite=Favoritos
navbar.recent=New and recently updated navbar.recent=Novos e Recentemente Atualizados
navbar.darkmode=Modo Escuro navbar.darkmode=Modo Escuro
navbar.language=Idiomas navbar.language=Idiomas
navbar.settings=Configurações navbar.settings=Configurações
@@ -266,14 +266,14 @@ home.viewPdf.title=Visualizar PDF
home.viewPdf.desc=Visualizar, anotar, adicionar texto ou imagens ao PDF. home.viewPdf.desc=Visualizar, anotar, adicionar texto ou imagens ao PDF.
viewPdf.tags=visualizar,ler,anotar,texto,imagem viewPdf.tags=visualizar,ler,anotar,texto,imagem
home.setFavorites=Set Favourites home.setFavorites=Adicionar Favoritos
home.hideFavorites=Hide Favourites home.hideFavorites=Ocultar Favoritos
home.showFavorites=Show Favourites home.showFavorites=Mostrar Favoritos
home.legacyHomepage=Old homepage home.legacyHomepage=Homepage Antiga
home.newHomePage=Try our new homepage! home.newHomePage=Experimente nossa nova Homepage!
home.alphabetical=Alphabetical home.alphabetical=Alfabética
home.globalPopularity=Global Popularity home.globalPopularity=Popularidade Global
home.sortBy=Sort by: home.sortBy=Ordenar por:
home.multiTool.title=Multiferramentas de PDF home.multiTool.title=Multiferramentas de PDF
home.multiTool.desc=Mesclar, girar, reorganizar, dividir, inserir e remover páginas. home.multiTool.desc=Mesclar, girar, reorganizar, dividir, inserir e remover páginas.
@@ -417,7 +417,7 @@ home.pageLayout.title=Layout de Múltiplas Páginas
home.pageLayout.desc=Mesclar várias páginas de um documento PDF em uma única página. home.pageLayout.desc=Mesclar várias páginas de um documento PDF em uma única página.
pageLayout.tags=mesclar,composto,vista-única,organizar pageLayout.tags=mesclar,composto,vista-única,organizar
home.scalePages.title=Ajustar Tamanho/Escala da Página home.scalePages.title=Ajustar Dimensões da Página
home.scalePages.desc=Alterar o tamanho/escala da página e/ou seu conteúdo. home.scalePages.desc=Alterar o tamanho/escala da página e/ou seu conteúdo.
scalePages.tags=redimensionar,modificar,dimensão,adaptar scalePages.tags=redimensionar,modificar,dimensão,adaptar
@@ -429,7 +429,7 @@ home.add-page-numbers.title=Adicionar Números de Página
home.add-page-numbers.desc=Adicionar números de página no documento, em um local definido. home.add-page-numbers.desc=Adicionar números de página no documento, em um local definido.
add-page-numbers.tags=paginar,rotular,organizar,índice add-page-numbers.tags=paginar,rotular,organizar,índice
home.auto-rename.title=Renomear Automaticamente o PDF home.auto-rename.title=Renomeação Automática do PDF
home.auto-rename.desc=Renomeia automaticamente o PDF com base no cabeçalho detectado. home.auto-rename.desc=Renomeia automaticamente o PDF com base no cabeçalho detectado.
auto-rename.tags=detecção-automática,baseado-em-cabeçalho,organizar,relabel auto-rename.tags=detecção-automática,baseado-em-cabeçalho,organizar,relabel
@@ -738,8 +738,8 @@ addPageNumbers.submit=Adicionar Números de Página
#auto-rename #auto-rename
auto-rename.title=Renomear Automaticamente o PDF auto-rename.title=Renomeação Automática do PDF
auto-rename.header=Renomear Automaticamente o PDF auto-rename.header=Renomeação Automática do PDF
auto-rename.submit=Renomeação Automática auto-rename.submit=Renomeação Automática
@@ -786,8 +786,8 @@ pageLayout.submit=Enviar
#scalePages #scalePages
scalePages.title=Ajustar Tamanho/Escala da Página scalePages.title=Ajustar Dimensões da Página
scalePages.header=Ajustar Tamanho/Escala da Página scalePages.header=Ajustar Dimensões da Página
scalePages.pageSize=Tamanho desejado do documento: scalePages.pageSize=Tamanho desejado do documento:
scalePages.keepPageSize=Tamanho Original scalePages.keepPageSize=Tamanho Original
scalePages.scaleFactor=Fator de zoom (corte) de uma página: scalePages.scaleFactor=Fator de zoom (corte) de uma página:

View File

@@ -48,7 +48,7 @@ window.tooltipSetup = () => {
tooltipElements.forEach((element) => { tooltipElements.forEach((element) => {
const tooltipText = element.getAttribute('title'); const tooltipText = element.getAttribute('title');
element.removeAttribute('title'); element.removeAttribute('title');
element.setAttribute('data-title', 'tooltipText'); element.setAttribute('data-title', tooltipText);
const customTooltip = document.createElement('div'); const customTooltip = document.createElement('div');
customTooltip.className = 'btn-tooltip'; customTooltip.className = 'btn-tooltip';
customTooltip.textContent = tooltipText; customTooltip.textContent = tooltipText;

View File

@@ -45,8 +45,6 @@
<li th:text="#{autoSplitPDF.selectText.3}"></li> <li th:text="#{autoSplitPDF.selectText.3}"></li>
<li th:text="#{autoSplitPDF.selectText.4}"></li> <li th:text="#{autoSplitPDF.selectText.4}"></li>
</ul> </ul>
<p><a th:href="@{'/files/Auto%20Splitter%20Divider%20(minimal).pdf'}" download
th:text="#{autoSplitPDF.dividerDownload1}"></a></p>
<p><a th:href="@{'/files/Auto%20Splitter%20Divider%20(with%20instructions).pdf'}" download <p><a th:href="@{'/files/Auto%20Splitter%20Divider%20(with%20instructions).pdf'}" download
th:text="#{autoSplitPDF.dividerDownload2}"></a></p> th:text="#{autoSplitPDF.dividerDownload2}"></a></p>
</div> </div>

View File

@@ -1,19 +1,19 @@
<th:block th:fragment="navElements"> <th:block th:fragment="navElements">
<div id="groupOrganize" class="feature-group"> <div id="groupOrganize" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.organize})}">
</div>
<div class="nav-group-container">
<div <div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.organize})}">
</div>
<div class="nav-group-container">
<div
th:replace="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'organize')}"> th:replace="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'organize')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry ('merge-pdfs', 'add_to_photos', 'home.merge.title', 'home.merge.desc', 'merge.tags', 'organize')}"> th:replace="~{fragments/navbarEntry :: navbarEntry ('merge-pdfs', 'add_to_photos', 'home.merge.title', 'home.merge.desc', 'merge.tags', 'organize')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry ('split-pdfs', 'cut', 'home.split.title', 'home.split.desc', 'split.tags', 'organize')}"></div> th:replace="~{fragments/navbarEntry :: navbarEntry ('split-pdfs', 'cut', 'home.split.title', 'home.split.desc', 'split.tags', 'organize')}">
<div </div>
<div
th:replace="~{fragments/navbarEntry :: navbarEntry('rotate-pdf', 'rotate_right', 'home.rotate.title', 'home.rotate.desc', 'rotate.tags', 'organize')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('rotate-pdf', 'rotate_right', 'home.rotate.title', 'home.rotate.desc', 'rotate.tags', 'organize')}">
</div> </div>
<div <div
@@ -37,15 +37,14 @@
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-single-page', 'looks_one', 'home.PdfToSinglePage.title', 'home.PdfToSinglePage.desc', 'PdfToSinglePage.tags', 'organize')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-single-page', 'looks_one', 'home.PdfToSinglePage.title', 'home.PdfToSinglePage.desc', 'PdfToSinglePage.tags', 'organize')}">
</div> </div>
</div>
</div> </div>
</div>
<div id="groupConvertTo" class="feature-group"> <div id="groupConvertTo" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertTo})}">
</div>
<div class="nav-group-container">
<div <div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertTo})}">
</div>
<div class="nav-group-container">
<div
th:replace="~{fragments/navbarEntry :: navbarEntry('img-to-pdf', 'picture_as_pdf', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'convertto')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('img-to-pdf', 'picture_as_pdf', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'convertto')}">
</div> </div>
<div <div
@@ -63,14 +62,13 @@
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('book-to-pdf', 'book', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags', 'convertto')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('book-to-pdf', 'book', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags', 'convertto')}">
</div> </div>
</div>
</div> </div>
<div id="groupConvertFrom" class="feature-group"> </div>
<div id="groupConvertFrom" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertFrom})}">
</div>
<div class="nav-group-container">
<div <div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertFrom})}">
</div>
<div class="nav-group-container">
<div
th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-img', 'photo_library', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags', 'convert')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-img', 'photo_library', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags', 'convert')}">
</div> </div>
<div <div
@@ -98,17 +96,16 @@
th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-book', 'book', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags', 'convert')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-book', 'book', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags', 'convert')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-markdown', 'markdown_copy', 'home.PDFToMarkdown.title', 'home.PDFToMarkdown.desc', 'PDFToMarkdown.tags', 'convert')}"> th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-markdown', 'markdown_copy', 'home.PDFToMarkdown.title', 'home.PDFToMarkdown.desc', 'PDFToMarkdown.tags', 'convert')}">
</div> </div>
</div> </div>
</div>
<div id="convertGroup" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertTo})}">
</div> </div>
<div id="convertGroup" class="feature-group"> <div class="nav-group-container">
<div <div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertTo})}">
</div>
<div class="nav-group-container">
<div
th:replace="~{fragments/navbarEntry :: navbarEntry('img-to-pdf', 'picture_as_pdf', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'convertto')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('img-to-pdf', 'picture_as_pdf', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'convertto')}">
</div> </div>
<div <div
@@ -126,12 +123,11 @@
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('book-to-pdf', 'book', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags', 'convertto')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('book-to-pdf', 'book', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags', 'convertto')}">
</div> </div>
</div> </div>
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertFrom})}">
</div>
<div class="nav-group-container">
<div <div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.convertFrom})}">
</div>
<div class="nav-group-container">
<div
th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-img', 'photo_library', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags', 'convert')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-img', 'photo_library', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags', 'convert')}">
</div> </div>
<div <div
@@ -159,13 +155,12 @@
th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-book', 'book', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags', 'convert')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('pdf-to-book', 'book', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags', 'convert')}">
</div> </div>
</div> </div>
</div>
<div id="groupSecurity" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.security})}">
</div> </div>
<div id="groupSecurity" class="feature-group"> <div class="nav-group-container">
<div <div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.security})}">
</div>
<div class="nav-group-container">
<div
th:replace="~{fragments/navbarEntry :: navbarEntry('add-password', 'lock', 'home.addPassword.title', 'home.addPassword.desc', 'addPassword.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('add-password', 'lock', 'home.addPassword.title', 'home.addPassword.desc', 'addPassword.tags', 'security')}">
</div> </div>
<div <div
@@ -182,34 +177,34 @@
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('validate-signature', 'verified', 'home.validateSignature.title', 'home.validateSignature.desc', 'validateSignature.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('validate-signature', 'verified', 'home.validateSignature.title', 'home.validateSignature.desc', 'validateSignature.tags', 'security')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('remove-cert-sign', 'remove_moderator', 'home.removeCertSign.title', 'home.removeCertSign.desc', 'removeCertSign.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('remove-cert-sign', 'remove_moderator', 'home.removeCertSign.title', 'home.removeCertSign.desc', 'removeCertSign.tags', 'security')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('sanitize-pdf', 'sanitizer', 'home.sanitizePdf.title', 'home.sanitizePdf.desc', 'sanitizePdf.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('sanitize-pdf', 'sanitizer', 'home.sanitizePdf.title', 'home.sanitizePdf.desc', 'sanitizePdf.tags', 'security')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntryCustom :: navbarEntry('auto-redact', '/images/redact-auto.svg#icon-redact-auto', 'home.autoRedact.title', 'home.autoRedact.desc', 'autoRedact.tags', 'security')}"> th:replace="~{fragments/navbarEntryCustom :: navbarEntry('auto-redact', '/images/redact-auto.svg#icon-redact-auto', 'home.autoRedact.title', 'home.autoRedact.desc', 'autoRedact.tags', 'security')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntryCustom :: navbarEntry('redact', '/images/redact-manual.svg#icon-redact-manual', 'home.redact.title', 'home.redact.desc', 'redact.tags', 'security')}"> th:replace="~{fragments/navbarEntryCustom :: navbarEntry('redact', '/images/redact-manual.svg#icon-redact-manual', 'home.redact.title', 'home.redact.desc', 'redact.tags', 'security')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('stamp', 'approval', 'home.AddStampRequest.title', 'home.AddStampRequest.desc', 'AddStampRequest.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('stamp', 'approval', 'home.AddStampRequest.title', 'home.AddStampRequest.desc', 'AddStampRequest.tags', 'security')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('add-watermark', 'water_drop', 'home.watermark.title', 'home.watermark.desc', 'watermark.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('add-watermark', 'water_drop', 'home.watermark.title', 'home.watermark.desc', 'watermark.tags', 'security')}">
</div>
</div> </div>
</div> </div>
</div>
<div id="groupView" class="feature-group"> <div id="groupView" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.edit})}"> <div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.edit})}">
</div> </div>
<div class="nav-group-container"> <div class="nav-group-container">
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('view-pdf', 'menu_book', 'home.viewPdf.title', 'home.viewPdf.desc', 'viewPdf.tags', 'other')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('view-pdf', 'menu_book', 'home.viewPdf.title', 'home.viewPdf.desc', 'viewPdf.tags', 'other')}">
</div> </div>
<div <div
@@ -246,16 +241,15 @@ th:replace="~{fragments/navbarEntryCustom :: navbarEntry('redact', '/images/reda
th:replace="~{fragments/navbarEntry :: navbarEntry('remove-image-pdf', 'remove_selection', 'home.removeImagePdf.title', 'home.removeImagePdf.desc', 'removeImagePdf.tags', 'other')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('remove-image-pdf', 'remove_selection', 'home.removeImagePdf.title', 'home.removeImagePdf.desc', 'removeImagePdf.tags', 'other')}">
</div> </div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry('replace-color-pdf', 'format_color_fill', 'home.replaceColorPdf.title', 'home.replaceColorPdf.desc', 'replaceColorPdf.tags', 'other')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('replace-and-invert-color-pdf', 'format_color_fill', 'home.replaceColorPdf.title', 'home.replaceColorPdf.desc', 'replaceColorPdf.tags', 'other')}">
</div>
</div> </div>
</div> </div>
<div id="groupAdvanced" class="feature-group"> </div>
<div id="groupAdvanced" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.advance})}">
</div>
<div class="nav-group-container">
<div <div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.advance})}">
</div>
<div class="nav-group-container">
<div
th:replace="~{fragments/navbarEntry :: navbarEntry('pipeline', 'family_history', 'home.pipeline.title', 'home.pipeline.desc', 'pipeline.tags', 'advance')}"> th:replace="~{fragments/navbarEntry :: navbarEntry('pipeline', 'family_history', 'home.pipeline.title', 'home.pipeline.desc', 'pipeline.tags', 'advance')}">
</div> </div>
<div <div
@@ -291,6 +285,6 @@ th:replace="~{fragments/navbarEntryCustom :: navbarEntry('redact', '/images/reda
<div <div
th:replace="~{fragments/navbarEntryCustom :: navbarEntry('split-pdf-by-chapters', '/images/split-chapters.svg#icon-split-chapters', 'home.splitPdfByChapters.title', 'home.splitPdfByChapters.desc', 'splitPdfByChapters.tags', 'advance')}"> th:replace="~{fragments/navbarEntryCustom :: navbarEntry('split-pdf-by-chapters', '/images/split-chapters.svg#icon-split-chapters', 'home.splitPdfByChapters.title', 'home.splitPdfByChapters.desc', 'splitPdfByChapters.tags', 'advance')}">
</div> </div>
</div> </div>
</div> </div>
</th:block> </th:block>

View File

@@ -27,15 +27,16 @@
<span class="material-symbols-rounded search-icon"> <span class="material-symbols-rounded search-icon">
search search
</span> </span>
<input type="text" id="searchBar" onkeyup="filterCards()" th:placeholder="#{home.searchBar}" autofocus> <input type="text" id="searchBar" onkeyup="filterCardsLegacy()" th:placeholder="#{home.searchBar}" autofocus>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<a href="home" onclick="setAsDefault('home')" style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;"> <a href="home" onclick="setAsDefault('home')"
<span th:text="#{home.newHomePage}"> style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;">
</span> <span th:text="#{home.newHomePage}">
<span class="material-symbols-rounded toggle-favourites" style="font-size: 2rem; margin-left: 0.2rem;" > </span>
home <span class="material-symbols-rounded toggle-favourites" style="font-size: 2rem; margin-left: 0.2rem;">
</span> home
</a> </span>
</a>
</div> </div>
<div id="filtersContainer"> <div id="filtersContainer">
<span class="material-symbols-rounded filter-button" onclick="toggleFavoritesOnly()"> <span class="material-symbols-rounded filter-button" onclick="toggleFavoritesOnly()">
@@ -70,7 +71,8 @@
</div> </div>
<div id="groupFavorites" class="feature-group-legacy"> <div id="groupFavorites" class="feature-group-legacy">
<div th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.favorite})}"> <div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.favorite})}">
</div> </div>
<div class="feature-group-container"> <div class="feature-group-container">
</div> </div>
@@ -248,7 +250,8 @@
</div> </div>
<div id="groupView" class="feature-group-legacy"> <div id="groupView" class="feature-group-legacy">
<div th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.edit})}"> <div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.edit})}">
</div> </div>
<div class="feature-group-container"> <div class="feature-group-container">
<div <div
@@ -289,7 +292,7 @@
th:replace="~{fragments/card :: card(id='remove-image-pdf', cardTitle=#{home.removeImagePdf.title}, cardText=#{home.removeImagePdf.desc}, cardLink='remove-image-pdf', toolIcon='remove_selection', tags=#{removeImagePdf.tags}, toolGroup='other')}"> th:replace="~{fragments/card :: card(id='remove-image-pdf', cardTitle=#{home.removeImagePdf.title}, cardText=#{home.removeImagePdf.desc}, cardLink='remove-image-pdf', toolIcon='remove_selection', tags=#{removeImagePdf.tags}, toolGroup='other')}">
</div> </div>
<div <div
th:replace="~{fragments/card :: card(id='replace-color-pdf', cardTitle=#{home.replaceColorPdf.title}, cardText=#{home.replaceColorPdf.desc}, cardLink='replace-and-invert-color-pdf', toolIcon='format_color_fill', tags=#{replaceColorPdf.tags}, toolGroup='other')}"> th:replace="~{fragments/card :: card(id='replace-and-invert-color-pdf', cardTitle=#{home.replaceColorPdf.title}, cardText=#{home.replaceColorPdf.desc}, cardLink='replace-and-invert-color-pdf', toolIcon='format_color_fill', tags=#{replaceColorPdf.tags}, toolGroup='other')}">
</div> </div>
</div> </div>
@@ -513,7 +516,7 @@
function setAsDefault(value) { function setAsDefault(value) {
localStorage.setItem('defaultView', value); localStorage.setItem('defaultView', value);
console.log(`Default view set to: ${value}`); console.log(`Default view set to: ${value}`);
} }
</script> </script>