refactor(pdf-conversion): consolidate and simplify profile handling

- Replaced direct field access with getter methods for profile attributes
- Added Lombok annotations to PdfaProfile and PdfXProfile enums for cleaner code
- Updated logging statements to utilize getter methods for consistency
- Improved validation and error message construction by standardizing access to profile attributes
- Simplified PdfaProfile and PdfXProfile implementations by removing redundant methods

Signed-off-by: Balázs Szücs <bszucs1209@gmail.com>
This commit is contained in:
Balázs Szücs 2025-11-08 12:33:17 +01:00
parent 69140b4b03
commit 42778aa2a4

View File

@ -70,6 +70,7 @@ import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation; 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.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.converters.PdfToPdfARequest; import stirling.software.SPDF.model.api.converters.PdfToPdfARequest;
@ -98,18 +99,21 @@ public class ConvertPDFToPDFA {
try { try {
validatePdfaOutput(tempPdfPath, profile); validatePdfaOutput(tempPdfPath, profile);
log.info("PDF/A validation passed for {} using {}", profile.displayName(), method); log.info(
"PDF/A validation passed for {} using {}",
profile.getDisplayName(),
method);
} catch (IOException e) { } catch (IOException e) {
log.warn( log.warn(
"PDF/A validation warning for {} using {}: {}", "PDF/A validation warning for {} using {}: {}",
profile.displayName(), profile.getDisplayName(),
method, method,
e.getMessage()); e.getMessage());
} }
} catch (IOException e) { } catch (IOException e) {
log.warn( log.warn(
"PDF/A validation warning for {} using {}: {}", "PDF/A validation warning for {} using {}: {}",
profile.displayName(), profile.getDisplayName(),
method, method,
e.getMessage()); e.getMessage());
} finally { } finally {
@ -158,9 +162,9 @@ public class ConvertPDFToPDFA {
command.add("--permit-file-read=" + inputPdf.toAbsolutePath()); command.add("--permit-file-read=" + inputPdf.toAbsolutePath());
command.add("--permit-file-read=" + pdfaDefFile.toAbsolutePath()); command.add("--permit-file-read=" + pdfaDefFile.toAbsolutePath());
command.add("--permit-file-write=" + workingDir.toAbsolutePath()); command.add("--permit-file-write=" + workingDir.toAbsolutePath());
command.add("-dPDFA=" + profile.part()); command.add("-dPDFA=" + profile.getPart());
command.add("-dPDFACompatibilityPolicy=" + PDFA_COMPATIBILITY_POLICY); command.add("-dPDFACompatibilityPolicy=" + PDFA_COMPATIBILITY_POLICY);
command.add("-dCompatibilityLevel=" + profile.compatibilityLevel()); command.add("-dCompatibilityLevel=" + profile.getCompatibilityLevel());
command.add("-sDEVICE=pdfwrite"); command.add("-sDEVICE=pdfwrite");
command.add("-sColorConversionStrategy=RGB"); command.add("-sColorConversionStrategy=RGB");
command.add("-dProcessColorModel=/DeviceRGB"); command.add("-dProcessColorModel=/DeviceRGB");
@ -183,7 +187,7 @@ public class ConvertPDFToPDFA {
private static void validatePdfaOutput(Path pdfPath, PdfaProfile profile) throws IOException { private static void validatePdfaOutput(Path pdfPath, PdfaProfile profile) throws IOException {
Optional<Format> format = profile.preflightFormat(); Optional<Format> format = profile.preflightFormat();
if (format.isEmpty()) { if (format.isEmpty()) {
log.debug("Skipping PDFBox preflight validation for {}", profile.displayName()); log.debug("Skipping PDFBox preflight validation for {}", profile.getDisplayName());
return; return;
} }
@ -202,13 +206,13 @@ public class ConvertPDFToPDFA {
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new IOException( throw new IOException(
"PDF/A preflight did not produce a PreflightDocument for " "PDF/A preflight did not produce a PreflightDocument for "
+ profile.displayName(), + profile.getDisplayName(),
e); e);
} }
if (document == null) { if (document == null) {
throw new IOException( throw new IOException(
"PDF/A preflight returned no document for " + profile.displayName()); "PDF/A preflight returned no document for " + profile.getDisplayName());
} }
try (PreflightDocument closeableDocument = document) { try (PreflightDocument closeableDocument = document) {
@ -221,12 +225,12 @@ public class ConvertPDFToPDFA {
throw new IOException(buildPreflightErrorMessage(e.getResult(), profile), e); throw new IOException(buildPreflightErrorMessage(e.getResult(), profile), e);
} catch (ValidationException e) { } catch (ValidationException e) {
throw new IOException( throw new IOException(
"PDF/A preflight validation failed for " + profile.displayName(), e); "PDF/A preflight validation failed for " + profile.getDisplayName(), e);
} }
} }
private static String buildPreflightErrorMessage(ValidationResult result, PdfaProfile profile) { private static String buildPreflightErrorMessage(ValidationResult result, PdfaProfile profile) {
String baseMessage = "PDF/A preflight validation failed for " + profile.displayName(); String baseMessage = "PDF/A preflight validation failed for " + profile.getDisplayName();
if (result == null) { if (result == null) {
return baseMessage + ": no detailed validation result available"; return baseMessage + ": no detailed validation result available";
} }
@ -273,7 +277,7 @@ public class ConvertPDFToPDFA {
Path workingDir, ColorProfiles colorProfiles, PdfaProfile profile) throws IOException { Path workingDir, ColorProfiles colorProfiles, PdfaProfile profile) throws IOException {
Path pdfaDefFile = workingDir.resolve("PDFA_def.ps"); Path pdfaDefFile = workingDir.resolve("PDFA_def.ps");
String title = "Converted to " + profile.displayName(); String title = "Converted to " + profile.getDisplayName();
String rgbProfilePath = colorProfiles.rgb().toAbsolutePath().toString().replace("\\", "/"); String rgbProfilePath = colorProfiles.rgb().toAbsolutePath().toString().replace("\\", "/");
String pdfaDefContent = String pdfaDefContent =
String.format( String.format(
@ -324,8 +328,8 @@ public class ConvertPDFToPDFA {
command.add("--permit-file-read=" + colorProfiles.gray().toAbsolutePath()); command.add("--permit-file-read=" + colorProfiles.gray().toAbsolutePath());
command.add("--permit-file-read=" + inputPdf.toAbsolutePath()); command.add("--permit-file-read=" + inputPdf.toAbsolutePath());
command.add("--permit-file-write=" + workingDir.toAbsolutePath()); command.add("--permit-file-write=" + workingDir.toAbsolutePath());
command.add("-dPDFX=" + profile.pdfxVersion()); command.add("-dPDFX=" + profile.getPdfxVersion());
command.add("-dCompatibilityLevel=" + profile.compatibilityLevel()); command.add("-dCompatibilityLevel=" + profile.getCompatibilityLevel());
command.add("-sDEVICE=pdfwrite"); command.add("-sDEVICE=pdfwrite");
command.add("-sColorConversionStrategy=RGB"); command.add("-sColorConversionStrategy=RGB");
command.add("-dProcessColorModel=/DeviceRGB"); command.add("-dProcessColorModel=/DeviceRGB");
@ -470,11 +474,11 @@ public class ConvertPDFToPDFA {
"Ghostscript is required for PDF/X conversion but is not available on the system"); "Ghostscript is required for PDF/X conversion but is not available on the system");
} }
log.info("Using Ghostscript for PDF/X conversion to {}", profile.displayName()); log.info("Using Ghostscript for PDF/X conversion to {}", profile.getDisplayName());
byte[] converted = convertWithGhostscriptX(inputPath, workingDir, profile); byte[] converted = convertWithGhostscriptX(inputPath, workingDir, profile);
String outputFilename = baseFileName + profile.outputSuffix(); String outputFilename = baseFileName + profile.outputSuffix();
log.info("PDF/X conversion completed successfully to {}", profile.displayName()); log.info("PDF/X conversion completed successfully to {}", profile.getDisplayName());
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
converted, outputFilename, MediaType.APPLICATION_PDF); converted, outputFilename, MediaType.APPLICATION_PDF);
@ -1063,7 +1067,7 @@ public class ConvertPDFToPDFA {
// Try Ghostscript first (preferred method) // Try Ghostscript first (preferred method)
if (isGhostscriptAvailable()) { if (isGhostscriptAvailable()) {
log.info("Using Ghostscript for PDF/A conversion to {}", profile.displayName()); log.info("Using Ghostscript for PDF/A conversion to {}", profile.getDisplayName());
try { try {
converted = convertWithGhostscript(inputPath, workingDir, profile); converted = convertWithGhostscript(inputPath, workingDir, profile);
String outputFilename = baseFileName + profile.outputSuffix(); String outputFilename = baseFileName + profile.outputSuffix();
@ -1100,7 +1104,7 @@ public class ConvertPDFToPDFA {
byte[] fileBytes; byte[] fileBytes;
Path loPdfPath = null; Path loPdfPath = null;
File preProcessedFile = null; File preProcessedFile = null;
int pdfaPart = profile.part(); int pdfaPart = profile.getPart();
try { try {
tempInputFile = inputPath; tempInputFile = inputPath;
@ -1167,6 +1171,7 @@ public class ConvertPDFToPDFA {
} }
} }
@Getter
private enum PdfaProfile { private enum PdfaProfile {
PDF_A_1B(1, "PDF/A-1b", "_PDFA-1b.pdf", "1.4", Format.PDF_A1B, "pdfa-1"), PDF_A_1B(1, "PDF/A-1b", "_PDFA-1b.pdf", "1.4", Format.PDF_A1B, "pdfa-1"),
PDF_A_2B(2, "PDF/A-2b", "_PDFA-2b.pdf", "1.7", null, "pdfa", "pdfa-2", "pdfa-2b"), PDF_A_2B(2, "PDF/A-2b", "_PDFA-2b.pdf", "1.7", null, "pdfa", "pdfa-2", "pdfa-2b"),
@ -1204,33 +1209,22 @@ public class ConvertPDFToPDFA {
String normalized = requestToken.trim().toLowerCase(Locale.ROOT); String normalized = requestToken.trim().toLowerCase(Locale.ROOT);
Optional<PdfaProfile> match = Optional<PdfaProfile> match =
Arrays.stream(values()) Arrays.stream(values())
.filter(profile -> profile.requestTokens.contains(normalized)) .filter(profile -> profile.getRequestTokens().contains(normalized))
.findFirst(); .findFirst();
return match.orElse(PDF_A_2B); return match.orElse(PDF_A_2B);
} }
int part() {
return part;
}
String displayName() {
return displayName;
}
String outputSuffix() { String outputSuffix() {
return suffix; return getSuffix();
}
String compatibilityLevel() {
return compatibilityLevel;
} }
Optional<Format> preflightFormat() { Optional<Format> preflightFormat() {
return Optional.ofNullable(preflightFormat); return Optional.ofNullable(getPreflightFormat());
} }
} }
@Getter
private enum PdfXProfile { private enum PdfXProfile {
PDF_X_1("PDF/X-1", "_PDFX-1.pdf", "1.3", "2001", "pdfx-1", "pdfx"), PDF_X_1("PDF/X-1", "_PDFX-1.pdf", "1.3", "2001", "pdfx-1", "pdfx"),
PDF_X_3("PDF/X-3", "_PDFX-3.pdf", "1.3", "2003", "pdfx-3"), PDF_X_3("PDF/X-3", "_PDFX-3.pdf", "1.3", "2003", "pdfx-3"),
@ -1265,26 +1259,14 @@ public class ConvertPDFToPDFA {
String normalized = requestToken.trim().toLowerCase(Locale.ROOT); String normalized = requestToken.trim().toLowerCase(Locale.ROOT);
Optional<PdfXProfile> match = Optional<PdfXProfile> match =
Arrays.stream(values()) Arrays.stream(values())
.filter(profile -> profile.requestTokens.contains(normalized)) .filter(profile -> profile.getRequestTokens().contains(normalized))
.findFirst(); .findFirst();
return match.orElse(PDF_X_4); return match.orElse(PDF_X_4);
} }
String displayName() {
return displayName;
}
String outputSuffix() { String outputSuffix() {
return suffix; return getSuffix();
}
String compatibilityLevel() {
return compatibilityLevel;
}
String pdfxVersion() {
return pdfxVersion;
} }
} }