deleteAttachment(@ModelAttribute DeleteAttachmentRequest request)
+ throws Exception {
+ MultipartFile fileInput = request.getFileInput();
+ String attachmentName = request.getAttachmentName();
+
+ if (attachmentName == null || attachmentName.isBlank()) {
+ throw ExceptionUtils.createIllegalArgumentException(
+ "error.attachmentNameRequired", "Attachment name cannot be null or empty");
+ }
+
+ try (PDDocument document = pdfDocumentFactory.load(request, false)) {
+ pdfAttachmentService.deleteAttachment(document, attachmentName);
+
+ return WebResponseUtils.pdfDocToWebResponse(
+ document,
+ GeneralUtils.generateFilename(
+ Filenames.toSimpleFileName(fileInput.getOriginalFilename()),
+ "_attachment_deleted.pdf"));
+ }
+ }
}
diff --git a/app/core/src/main/java/stirling/software/SPDF/exception/GlobalExceptionHandler.java b/app/core/src/main/java/stirling/software/SPDF/exception/GlobalExceptionHandler.java
index fd9224a41..82d1b2bb9 100644
--- a/app/core/src/main/java/stirling/software/SPDF/exception/GlobalExceptionHandler.java
+++ b/app/core/src/main/java/stirling/software/SPDF/exception/GlobalExceptionHandler.java
@@ -12,6 +12,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -23,6 +24,7 @@ import org.springframework.web.multipart.support.MissingServletRequestPartExcept
import org.springframework.web.servlet.NoHandlerFoundException;
import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -808,6 +810,56 @@ public class GlobalExceptionHandler {
.body(problemDetail);
}
+ /**
+ * Handle 406 Not Acceptable errors when error responses cannot match client Accept header.
+ *
+ * When thrown: When the client sends Accept: application/pdf but the server needs to return
+ * a JSON error response (e.g., when an attachment is not found).
+ *
+ *
This handler writes directly to HttpServletResponse to bypass Spring's content negotiation
+ * and ensure error responses are always delivered as JSON.
+ *
+ * @param ex the HttpMediaTypeNotAcceptableException
+ * @param request the HTTP servlet request
+ * @param response the HTTP servlet response
+ */
+ @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
+ public void handleMediaTypeNotAcceptable(
+ HttpMediaTypeNotAcceptableException ex,
+ HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException {
+
+ log.warn(
+ "Media type not acceptable at {}: client accepts {}, server supports {}",
+ request.getRequestURI(),
+ request.getHeader("Accept"),
+ ex.getSupportedMediaTypes());
+
+ // Write JSON error response directly, bypassing content negotiation
+ response.setStatus(HttpStatus.NOT_ACCEPTABLE.value());
+ response.setContentType("application/problem+json");
+ response.setCharacterEncoding("UTF-8");
+
+ String errorJson =
+ String.format(
+ """
+ {
+ "type": "about:blank",
+ "title": "Not Acceptable",
+ "status": 406,
+ "detail": "The requested resource could not be returned in an acceptable format. Error responses are returned as JSON.",
+ "instance": "%s",
+ "timestamp": "%s",
+ "hints": ["Error responses are always returned as application/json or application/problem+json", "Set Accept header to include application/json for proper error handling"]
+ }
+ """,
+ request.getRequestURI(), Instant.now().toString());
+
+ response.getWriter().write(errorJson);
+ response.getWriter().flush();
+ }
+
// ===========================================================================================
// JAVA STANDARD EXCEPTIONS
// ===========================================================================================
@@ -963,9 +1015,8 @@ public class GlobalExceptionHandler {
// Check if this RuntimeException wraps a typed exception from job execution
Throwable cause = ex.getCause();
- if (cause instanceof BaseAppException) {
+ if (cause instanceof BaseAppException appEx) {
// Delegate to specific BaseAppException handlers
- BaseAppException appEx = (BaseAppException) cause;
if (appEx instanceof PdfPasswordException) {
return handlePdfPassword((PdfPasswordException) appEx, request);
} else if (appEx instanceof PdfCorruptedException
@@ -979,9 +1030,8 @@ public class GlobalExceptionHandler {
} else {
return handleBaseApp(appEx, request);
}
- } else if (cause instanceof BaseValidationException) {
+ } else if (cause instanceof BaseValidationException valEx) {
// Delegate to validation exception handlers
- BaseValidationException valEx = (BaseValidationException) cause;
if (valEx instanceof CbrFormatException
|| valEx instanceof CbzFormatException
|| valEx instanceof EmlFormatException) {
@@ -992,6 +1042,9 @@ public class GlobalExceptionHandler {
} else if (cause instanceof IOException) {
// Unwrap and handle IOException (may contain PDF-specific errors)
return handleIOException((IOException) cause, request);
+ } else if (cause instanceof IllegalArgumentException) {
+ // Unwrap and handle IllegalArgumentException (business logic validation errors)
+ return handleIllegalArgument((IllegalArgumentException) cause, request);
}
// Not a wrapped exception - treat as unexpected error
diff --git a/app/core/src/main/java/stirling/software/SPDF/model/api/misc/AddAttachmentRequest.java b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/AddAttachmentRequest.java
index cf85451f4..48a749098 100644
--- a/app/core/src/main/java/stirling/software/SPDF/model/api/misc/AddAttachmentRequest.java
+++ b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/AddAttachmentRequest.java
@@ -20,4 +20,10 @@ public class AddAttachmentRequest extends PDFFile {
requiredMode = Schema.RequiredMode.REQUIRED,
format = "binary")
private List attachments;
+
+ @Schema(
+ description = "Convert the resulting PDF to PDF/A-3b format after adding attachments",
+ requiredMode = Schema.RequiredMode.NOT_REQUIRED,
+ defaultValue = "false")
+ private boolean convertToPdfA3b = false;
}
diff --git a/app/core/src/main/java/stirling/software/SPDF/model/api/misc/AttachmentInfo.java b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/AttachmentInfo.java
new file mode 100644
index 000000000..b80139be4
--- /dev/null
+++ b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/AttachmentInfo.java
@@ -0,0 +1,17 @@
+package stirling.software.SPDF.model.api.misc;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttachmentInfo {
+ private String filename;
+ private Long size;
+ private String contentType;
+ private String description;
+ private String creationDate;
+ private String modificationDate;
+}
diff --git a/app/core/src/main/java/stirling/software/SPDF/model/api/misc/DeleteAttachmentRequest.java b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/DeleteAttachmentRequest.java
new file mode 100644
index 000000000..22b7d017b
--- /dev/null
+++ b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/DeleteAttachmentRequest.java
@@ -0,0 +1,18 @@
+package stirling.software.SPDF.model.api.misc;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import stirling.software.common.model.api.PDFFile;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DeleteAttachmentRequest extends PDFFile {
+
+ @Schema(
+ description = "The name of the attachment to delete",
+ requiredMode = Schema.RequiredMode.REQUIRED)
+ private String attachmentName;
+}
diff --git a/app/core/src/main/java/stirling/software/SPDF/model/api/misc/ListAttachmentsRequest.java b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/ListAttachmentsRequest.java
new file mode 100644
index 000000000..f30fc7540
--- /dev/null
+++ b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/ListAttachmentsRequest.java
@@ -0,0 +1,10 @@
+package stirling.software.SPDF.model.api.misc;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import stirling.software.common.model.api.PDFFile;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ListAttachmentsRequest extends PDFFile {}
diff --git a/app/core/src/main/java/stirling/software/SPDF/model/api/misc/RenameAttachmentRequest.java b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/RenameAttachmentRequest.java
new file mode 100644
index 000000000..07731fce3
--- /dev/null
+++ b/app/core/src/main/java/stirling/software/SPDF/model/api/misc/RenameAttachmentRequest.java
@@ -0,0 +1,23 @@
+package stirling.software.SPDF.model.api.misc;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import stirling.software.common.model.api.PDFFile;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class RenameAttachmentRequest extends PDFFile {
+
+ @Schema(
+ description = "The current name of the attachment to rename",
+ requiredMode = Schema.RequiredMode.REQUIRED)
+ private String attachmentName;
+
+ @Schema(
+ description = "The new name for the attachment",
+ requiredMode = Schema.RequiredMode.REQUIRED)
+ private String newName;
+}
diff --git a/app/core/src/main/java/stirling/software/SPDF/service/AttachmentService.java b/app/core/src/main/java/stirling/software/SPDF/service/AttachmentService.java
index 029e7086c..0f73632d3 100644
--- a/app/core/src/main/java/stirling/software/SPDF/service/AttachmentService.java
+++ b/app/core/src/main/java/stirling/software/SPDF/service/AttachmentService.java
@@ -8,6 +8,7 @@ import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,6 +37,9 @@ import io.github.pixee.security.Filenames;
import lombok.extern.slf4j.Slf4j;
+import stirling.software.SPDF.model.api.misc.AttachmentInfo;
+import stirling.software.common.util.ExceptionUtils;
+
@Slf4j
@Service
public class AttachmentService implements AttachmentServiceInterface {
@@ -216,6 +220,142 @@ public class AttachmentService implements AttachmentServiceInterface {
}
}
+ @Override
+ public List listAttachments(PDDocument document) throws IOException {
+ List attachments = new ArrayList<>();
+
+ PDDocumentCatalog catalog = document.getDocumentCatalog();
+ if (catalog == null) {
+ return attachments;
+ }
+
+ PDDocumentNameDictionary documentNames = catalog.getNames();
+ if (documentNames == null) {
+ return attachments;
+ }
+
+ PDEmbeddedFilesNameTreeNode embeddedFilesTree = documentNames.getEmbeddedFiles();
+ if (embeddedFilesTree == null) {
+ return attachments;
+ }
+
+ Map embeddedFiles = new LinkedHashMap<>();
+ collectEmbeddedFiles(embeddedFilesTree, embeddedFiles);
+
+ for (Map.Entry entry : embeddedFiles.entrySet()) {
+ PDComplexFileSpecification fileSpecification = entry.getValue();
+ PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpecification);
+
+ if (embeddedFile != null) {
+ String filename = determineFilename(entry.getKey(), fileSpecification);
+ String description = fileSpecification.getFileDescription();
+ String contentType = embeddedFile.getSubtype();
+ Long size = (long) embeddedFile.getSize();
+
+ String creationDate = null;
+ if (embeddedFile.getCreationDate() != null) {
+ creationDate = embeddedFile.getCreationDate().getTime().toString();
+ }
+
+ String modificationDate = null;
+ if (embeddedFile.getModDate() != null) {
+ modificationDate = embeddedFile.getModDate().getTime().toString();
+ }
+
+ AttachmentInfo attachmentInfo =
+ new AttachmentInfo(
+ filename,
+ size,
+ contentType,
+ description,
+ creationDate,
+ modificationDate);
+
+ attachments.add(attachmentInfo);
+ }
+ }
+
+ return attachments;
+ }
+
+ @Override
+ public PDDocument renameAttachment(PDDocument document, String attachmentName, String newName)
+ throws IOException {
+ PDEmbeddedFilesNameTreeNode embeddedFilesTree = getEmbeddedFilesTree(document);
+
+ Map allEmbeddedFiles = new LinkedHashMap<>();
+ collectEmbeddedFiles(embeddedFilesTree, allEmbeddedFiles);
+
+ PDComplexFileSpecification fileToRename = null;
+ String keyToRename = null;
+
+ for (Map.Entry entry : allEmbeddedFiles.entrySet()) {
+ String currentName = determineFilename(entry.getKey(), entry.getValue());
+ if (currentName.equals(attachmentName)) {
+ fileToRename = entry.getValue();
+ keyToRename = entry.getKey();
+ break;
+ }
+ }
+
+ if (fileToRename == null || keyToRename == null) {
+ log.warn("Attachment '{}' not found for renaming", attachmentName);
+ throw ExceptionUtils.createIllegalArgumentException(
+ "error.attachmentNotFound",
+ "Attachment ''{0}'' not found for renaming",
+ attachmentName);
+ }
+
+ fileToRename.setFile(newName);
+ fileToRename.setFileUnicode(newName);
+
+ allEmbeddedFiles.remove(keyToRename);
+ allEmbeddedFiles.put(newName, fileToRename);
+
+ embeddedFilesTree.setKids(null);
+
+ embeddedFilesTree.setNames(allEmbeddedFiles);
+ log.info("Renamed attachment from '{}' to '{}'", attachmentName, newName);
+
+ return document;
+ }
+
+ @Override
+ public PDDocument deleteAttachment(PDDocument document, String attachmentName)
+ throws IOException {
+ PDEmbeddedFilesNameTreeNode embeddedFilesTree = getEmbeddedFilesTree(document);
+
+ Map allEmbeddedFiles = new LinkedHashMap<>();
+ collectEmbeddedFiles(embeddedFilesTree, allEmbeddedFiles);
+
+ String keyToRemove = null;
+
+ for (Map.Entry entry : allEmbeddedFiles.entrySet()) {
+ String currentName = determineFilename(entry.getKey(), entry.getValue());
+ if (currentName.equals(attachmentName)) {
+ keyToRemove = entry.getKey();
+ break;
+ }
+ }
+
+ if (keyToRemove == null) {
+ log.warn("Attachment '{}' not found for deletion", attachmentName);
+ throw ExceptionUtils.createIllegalArgumentException(
+ "error.attachmentNotFound",
+ "Attachment ''{0}'' not found for deletion",
+ attachmentName);
+ }
+
+ allEmbeddedFiles.remove(keyToRemove);
+
+ embeddedFilesTree.setKids(null);
+
+ embeddedFilesTree.setNames(allEmbeddedFiles);
+ log.info("Deleted attachment: '{}'", attachmentName);
+
+ return document;
+ }
+
private String sanitizeFilename(String candidate) {
String sanitized = Filenames.toSimpleFileName(candidate);
if (StringUtils.isBlank(sanitized)) {
diff --git a/app/core/src/main/java/stirling/software/SPDF/service/AttachmentServiceInterface.java b/app/core/src/main/java/stirling/software/SPDF/service/AttachmentServiceInterface.java
index f9e1bfb67..2a6973107 100644
--- a/app/core/src/main/java/stirling/software/SPDF/service/AttachmentServiceInterface.java
+++ b/app/core/src/main/java/stirling/software/SPDF/service/AttachmentServiceInterface.java
@@ -7,10 +7,19 @@ import java.util.Optional;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.web.multipart.MultipartFile;
+import stirling.software.SPDF.model.api.misc.AttachmentInfo;
+
public interface AttachmentServiceInterface {
PDDocument addAttachment(PDDocument document, List attachments)
throws IOException;
Optional extractAttachments(PDDocument document) throws IOException;
+
+ List listAttachments(PDDocument document) throws IOException;
+
+ PDDocument renameAttachment(PDDocument document, String attachmentName, String newName)
+ throws IOException;
+
+ PDDocument deleteAttachment(PDDocument document, String attachmentName) throws IOException;
}
diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/AttachmentControllerTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/AttachmentControllerTest.java
index afec68778..fe0e2ca2d 100644
--- a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/AttachmentControllerTest.java
+++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/AttachmentControllerTest.java
@@ -67,16 +67,16 @@ class AttachmentControllerTest {
}
@Test
- void addAttachments_Success() throws IOException {
+ void addAttachments_Success() throws Exception {
List attachments = List.of(attachment1, attachment2);
request.setAttachments(attachments);
request.setFileInput(pdfFile);
ResponseEntity expectedResponse =
ResponseEntity.ok("modified PDF content".getBytes());
- when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
+ when(pdfDocumentFactory.load(request, false)).thenReturn(mockDocument);
when(pdfAttachmentService.addAttachment(mockDocument, attachments))
- .thenReturn(modifiedMockDocument);
+ .thenReturn(mockDocument);
try (MockedStatic mockedWebResponseUtils =
mockStatic(WebResponseUtils.class)) {
@@ -84,8 +84,7 @@ class AttachmentControllerTest {
.when(
() ->
WebResponseUtils.pdfDocToWebResponse(
- eq(modifiedMockDocument),
- eq("test_with_attachments.pdf")))
+ eq(mockDocument), eq("test_with_attachments.pdf")))
.thenReturn(expectedResponse);
ResponseEntity response = attachmentController.addAttachments(request);
@@ -93,22 +92,22 @@ class AttachmentControllerTest {
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
- verify(pdfDocumentFactory).load(pdfFile, false);
+ verify(pdfDocumentFactory).load(request, false);
verify(pdfAttachmentService).addAttachment(mockDocument, attachments);
}
}
@Test
- void addAttachments_SingleAttachment() throws IOException {
+ void addAttachments_SingleAttachment() throws Exception {
List attachments = List.of(attachment1);
request.setAttachments(attachments);
request.setFileInput(pdfFile);
ResponseEntity expectedResponse =
ResponseEntity.ok("modified PDF content".getBytes());
- when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
+ when(pdfDocumentFactory.load(request, false)).thenReturn(mockDocument);
when(pdfAttachmentService.addAttachment(mockDocument, attachments))
- .thenReturn(modifiedMockDocument);
+ .thenReturn(mockDocument);
try (MockedStatic mockedWebResponseUtils =
mockStatic(WebResponseUtils.class)) {
@@ -116,8 +115,7 @@ class AttachmentControllerTest {
.when(
() ->
WebResponseUtils.pdfDocToWebResponse(
- eq(modifiedMockDocument),
- eq("test_with_attachments.pdf")))
+ eq(mockDocument), eq("test_with_attachments.pdf")))
.thenReturn(expectedResponse);
ResponseEntity response = attachmentController.addAttachments(request);
@@ -125,33 +123,33 @@ class AttachmentControllerTest {
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
- verify(pdfDocumentFactory).load(pdfFile, false);
+ verify(pdfDocumentFactory).load(request, false);
verify(pdfAttachmentService).addAttachment(mockDocument, attachments);
}
}
@Test
- void addAttachments_IOExceptionFromPDFLoad() throws IOException {
+ void addAttachments_IOExceptionFromPDFLoad() throws Exception {
List attachments = List.of(attachment1);
request.setAttachments(attachments);
request.setFileInput(pdfFile);
IOException ioException = new IOException("Failed to load PDF");
- when(pdfDocumentFactory.load(pdfFile, false)).thenThrow(ioException);
+ when(pdfDocumentFactory.load(request, false)).thenThrow(ioException);
assertThrows(IOException.class, () -> attachmentController.addAttachments(request));
- verify(pdfDocumentFactory).load(pdfFile, false);
+ verify(pdfDocumentFactory).load(request, false);
verifyNoInteractions(pdfAttachmentService);
}
@Test
- void addAttachments_IOExceptionFromAttachmentService() throws IOException {
+ void addAttachments_IOExceptionFromAttachmentService() throws Exception {
List attachments = List.of(attachment1);
request.setAttachments(attachments);
request.setFileInput(pdfFile);
IOException ioException = new IOException("Failed to add attachment");
- when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
+ when(pdfDocumentFactory.load(request, false)).thenReturn(mockDocument);
when(pdfAttachmentService.addAttachment(mockDocument, attachments)).thenThrow(ioException);
assertThrows(IOException.class, () -> attachmentController.addAttachments(request));
diff --git a/frontend/public/locales/en-GB/translation.toml b/frontend/public/locales/en-GB/translation.toml
index f3bd4c5d6..6d27d6f84 100644
--- a/frontend/public/locales/en-GB/translation.toml
+++ b/frontend/public/locales/en-GB/translation.toml
@@ -1394,6 +1394,11 @@ header = "Add Attachments"
add = "Add Attachment"
remove = "Remove Attachment"
embed = "Embed Attachment"
+convertToPdfA3b = "Convert to PDF/A-3b"
+convertToPdfA3bDescription = "Creates an archival PDF with embedded attachments"
+convertToPdfA3bTooltip = "PDF/A-3b is an archival format ensuring long-term preservation. It allows embedding arbitrary file formats as attachments. Conversion requires Ghostscript and may take longer for large files."
+convertToPdfA3bTooltipHeader = "About PDF/A-3b Conversion"
+convertToPdfA3bTooltipTitle = "What it does"
submit = "Add Attachments"
[watermark]
diff --git a/frontend/src/core/components/tools/addAttachments/AddAttachmentsSettings.tsx b/frontend/src/core/components/tools/addAttachments/AddAttachmentsSettings.tsx
index 33d9e8b33..2108c3c0b 100644
--- a/frontend/src/core/components/tools/addAttachments/AddAttachmentsSettings.tsx
+++ b/frontend/src/core/components/tools/addAttachments/AddAttachmentsSettings.tsx
@@ -1,13 +1,14 @@
/**
* AddAttachmentsSettings - Shared settings component for both tool UI and automation
*
- * Allows selecting files to attach to PDFs.
+ * Allows selecting files to attach to PDFs with optional PDF/A-3b conversion support.
*/
-import { Stack, Text, Group, ActionIcon, ScrollArea, Button } from "@mantine/core";
+import { Stack, Text, Group, ActionIcon, ScrollArea, Button, Checkbox } from "@mantine/core";
import { useTranslation } from "react-i18next";
import { AddAttachmentsParameters } from "@app/hooks/tools/addAttachments/useAddAttachmentsParameters";
import LocalIcon from "@app/components/shared/LocalIcon";
+import { Tooltip } from "@app/components/shared/Tooltip";
interface AddAttachmentsSettingsProps {
parameters: AddAttachmentsParameters;
@@ -103,6 +104,40 @@ const AddAttachmentsSettings = ({ parameters, onParameterChange, disabled = fals
)}
+
+ {/* PDF/A-3b conversion option with informative tooltip */}
+
+
+ {t("attachments.convertToPdfA3b", "Convert to PDF/A-3b")}
+
+
+
+
+ }
+ description={t("attachments.convertToPdfA3bDescription", "Creates an archival PDF with embedded attachments")}
+ checked={parameters.convertToPdfA3b}
+ onChange={(event) => onParameterChange('convertToPdfA3b', event.currentTarget.checked)}
+ disabled={disabled}
+ styles={{ root: { flex: 1 } }}
+ />
+
);
};
diff --git a/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsOperation.ts b/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsOperation.ts
index 9785fa998..4614a9cbf 100644
--- a/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsOperation.ts
+++ b/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsOperation.ts
@@ -16,6 +16,8 @@ const buildFormData = (parameters: AddAttachmentsParameters, file: File): FormDa
if (attachment) formData.append("attachments", attachment);
});
+ formData.append("convertToPdfA3b", String(parameters.convertToPdfA3b));
+
return formData;
};
diff --git a/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsParameters.ts b/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsParameters.ts
index ce21e3869..1e66120b7 100644
--- a/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsParameters.ts
+++ b/frontend/src/core/hooks/tools/addAttachments/useAddAttachmentsParameters.ts
@@ -2,10 +2,12 @@ import { useState } from 'react';
export interface AddAttachmentsParameters {
attachments: File[];
+ convertToPdfA3b: boolean;
}
const defaultParameters: AddAttachmentsParameters = {
- attachments: []
+ attachments: [],
+ convertToPdfA3b: false
};
export const useAddAttachmentsParameters = () => {
@@ -33,3 +35,5 @@ export const useAddAttachmentsParameters = () => {
validateParameters
};
};
+
+export const DEFAULT_ADD_ATTACHMENTS_PARAMETERS: AddAttachmentsParameters = defaultParameters;