chore: reformat Java codebase and centralize Spotless config (#3723)

# Description of Changes

Please provide a summary of the changes, including:

- Extracted the `googleJavaFormatVersion` into a centralized Gradle
property for easier management across modules.
- Added consistent `spotless` formatting configuration to `common`,
`proprietary`, and `stirling-pdf` modules.
- Applied automatic import ordering and removed unused imports in
numerous Java files.
- Reordered and grouped imports consistently, improving overall code
readability.
- Removed excessive blank lines and standardized spacing.
- Ensured a uniform coding style throughout the codebase using Spotless
and Google Java Format with AOSP style.

---

## 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)
- [x] I have performed a self-review of my own code
- [x] 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.
This commit is contained in:
Ludy
2025-06-16 20:44:11 +01:00
committed by GitHub
parent da2473c784
commit fe47cac608
270 changed files with 767 additions and 1437 deletions
+7 -2
View File
@@ -2,7 +2,12 @@
bootRun {
enabled = false
}
spotless {
java {
target sourceSets.main.allJava
googleJavaFormat(googleJavaFormatVersion).aosp()
}
}
dependencies {
api 'org.springframework.boot:spring-boot-starter-web'
api 'org.springframework.boot:spring-boot-starter-thymeleaf'
@@ -17,4 +22,4 @@ dependencies {
api 'org.snakeyaml:snakeyaml-engine:2.9'
api "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8"
api 'jakarta.mail:jakarta.mail-api:2.1.3'
}
}
@@ -4,10 +4,8 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
/** A custom RandomAccessRead implementation that deletes the file when closed */
@Slf4j
@@ -248,17 +248,16 @@ public class AppConfig {
return applicationProperties.getSystem().getDatasource();
}
@Bean(name = "runningProOrHigher")
@Profile("default")
public boolean runningProOrHigher() {
return false;
return false;
}
@Bean(name = "runningEE")
@Profile("default")
public boolean runningEnterprise() {
return false;
return false;
}
@Bean(name = "GoogleDriveEnabled")
@@ -273,10 +272,9 @@ public class AppConfig {
return "NORMAL";
}
@Bean(name = "disablePixel")
public boolean disablePixel() {
return Boolean.parseBoolean(env.getProperty("DISABLE_PIXEL", "false"));
return Boolean.parseBoolean(env.getProperty("DISABLE_PIXEL", "false"));
}
@Bean(name = "machineType")
@@ -10,9 +10,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.util.YamlHelper;
/**
@@ -3,16 +3,13 @@ package stirling.software.common.configuration;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
import org.thymeleaf.templateresource.FileTemplateResource;
import org.thymeleaf.templateresource.ITemplateResource;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.InputStreamTemplateResource;
@Slf4j
@@ -2,7 +2,6 @@ package stirling.software.common.configuration;
import java.io.File;
import java.nio.file.Paths;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@@ -1,15 +1,12 @@
package stirling.software.common.configuration;
import com.posthog.java.PostHog;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.posthog.java.PostHog;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
@Configuration
@Slf4j
public class PostHogConfig {
@@ -1,10 +1,8 @@
package stirling.software.common.configuration;
import org.springframework.stereotype.Component;
import com.posthog.java.PostHogLogger;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@@ -2,13 +2,10 @@ package stirling.software.common.configuration;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.ApplicationProperties.CustomPaths.Operations;
import stirling.software.common.model.ApplicationProperties.CustomPaths.Pipeline;
@@ -1,7 +1,6 @@
package stirling.software.common.configuration;
import java.util.Properties;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
@@ -12,7 +12,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
@@ -24,13 +28,6 @@ import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.stereotype.Component;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.configuration.YamlPropertySourceFactory;
import stirling.software.common.model.exception.UnsupportedProviderException;
@@ -5,7 +5,6 @@ import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import org.thymeleaf.templateresource.ITemplateResource;
public class InputStreamTemplateResource implements ITemplateResource {
@@ -1,7 +1,6 @@
package stirling.software.common.model;
import java.util.Calendar;
import lombok.Builder;
import lombok.Data;
@@ -1,11 +1,9 @@
package stirling.software.common.model.api;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.web.multipart.MultipartFile;
@Data
@EqualsAndHashCode
@@ -1,12 +1,10 @@
package stirling.software.common.model.api;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
@Data
@NoArgsConstructor
@@ -1,10 +1,8 @@
package stirling.software.common.model.api.converters;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@@ -1,7 +1,6 @@
package stirling.software.common.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -2,7 +2,6 @@ package stirling.software.common.model.enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -2,9 +2,7 @@ package stirling.software.common.model.oauth2;
import java.util.ArrayList;
import java.util.Collection;
import lombok.NoArgsConstructor;
import stirling.software.common.model.enumeration.UsernameAttribute;
@NoArgsConstructor
@@ -2,9 +2,7 @@ package stirling.software.common.model.oauth2;
import java.util.ArrayList;
import java.util.Collection;
import lombok.NoArgsConstructor;
import stirling.software.common.model.enumeration.UsernameAttribute;
@NoArgsConstructor
@@ -2,9 +2,7 @@ package stirling.software.common.model.oauth2;
import java.util.ArrayList;
import java.util.Collection;
import lombok.NoArgsConstructor;
import stirling.software.common.model.enumeration.UsernameAttribute;
@NoArgsConstructor
@@ -5,10 +5,8 @@ import static stirling.software.common.model.enumeration.UsernameAttribute.EMAIL
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import lombok.Data;
import lombok.NoArgsConstructor;
import stirling.software.common.model.enumeration.UsernameAttribute;
import stirling.software.common.model.exception.UnsupportedClaimException;
@@ -8,7 +8,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.atomic.AtomicLong;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.examples.util.DeletingRandomAccessFile;
import org.apache.pdfbox.io.IOUtils;
@@ -18,10 +19,6 @@ import org.apache.pdfbox.io.ScratchFile;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.api.PDFFile;
/**
@@ -1,12 +1,10 @@
package stirling.software.common.service;
import java.util.Calendar;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.PdfMetadata;
@@ -1,5 +1,6 @@
package stirling.software.common.service;
import com.posthog.java.PostHog;
import java.io.File;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
@@ -16,15 +17,11 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import com.posthog.java.PostHog;
import stirling.software.common.model.ApplicationProperties;
@Service
@@ -3,7 +3,6 @@ package stirling.software.common.util;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
public class CheckProgramInstall {
@@ -19,7 +19,10 @@ import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -35,11 +38,6 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.api.converters.EmlToPdfRequest;
@Slf4j
@@ -49,7 +47,8 @@ public class EmlToPdf {
private static final class StyleConstants {
// Font and layout constants
static final int DEFAULT_FONT_SIZE = 12;
static final String DEFAULT_FONT_FAMILY = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif";
static final String DEFAULT_FONT_FAMILY =
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif";
static final float DEFAULT_LINE_HEIGHT = 1.4f;
static final String DEFAULT_ZOOM = "1.0";
@@ -76,7 +75,8 @@ public class EmlToPdf {
}
private static final class MimeConstants {
static final Pattern MIME_ENCODED_PATTERN = Pattern.compile("=\\?([^?]+)\\?([BbQq])\\?([^?]*)\\?=");
static final Pattern MIME_ENCODED_PATTERN =
Pattern.compile("=\\?([^?]+)\\?([BbQq])\\?([^?]*)\\?=");
static final String PAPERCLIP_EMOJI = "\uD83D\uDCCE"; // 📎
static final String ATTACHMENT_ICON_PLACEHOLDER = "icon";
@@ -113,7 +113,8 @@ public class EmlToPdf {
return jakartaMailAvailable;
}
public static String convertEmlToHtml(byte[] emlBytes, EmlToPdfRequest request) throws IOException {
public static String convertEmlToHtml(byte[] emlBytes, EmlToPdfRequest request)
throws IOException {
validateEmlInput(emlBytes);
if (isJakartaMailAvailable()) {
@@ -147,11 +148,14 @@ public class EmlToPdf {
}
// Convert HTML to PDF
byte[] pdfBytes = convertHtmlToPdf(weasyprintPath, request, htmlContent, disableSanitize);
byte[] pdfBytes =
convertHtmlToPdf(weasyprintPath, request, htmlContent, disableSanitize);
// Attach files if available and requested
if (shouldAttachFiles(emailContent, request)) {
pdfBytes = attachFilesToPdf(pdfBytes, emailContent.getAttachments(), pdfDocumentFactory);
pdfBytes =
attachFilesToPdf(
pdfBytes, emailContent.getAttachments(), pdfDocumentFactory);
}
return pdfBytes;
@@ -177,16 +181,20 @@ public class EmlToPdf {
private static boolean shouldAttachFiles(EmailContent emailContent, EmlToPdfRequest request) {
return emailContent != null
&& request != null
&& request.isIncludeAttachments()
&& !emailContent.getAttachments().isEmpty();
&& request != null
&& request.isIncludeAttachments()
&& !emailContent.getAttachments().isEmpty();
}
private static byte[] convertHtmlToPdf(String weasyprintPath, EmlToPdfRequest request,
String htmlContent, boolean disableSanitize)
private static byte[] convertHtmlToPdf(
String weasyprintPath,
EmlToPdfRequest request,
String htmlContent,
boolean disableSanitize)
throws IOException, InterruptedException {
stirling.software.common.model.api.converters.HTMLToPdfRequest htmlRequest = createHtmlRequest(request);
stirling.software.common.model.api.converters.HTMLToPdfRequest htmlRequest =
createHtmlRequest(request);
try {
return FileToPdf.convertHtmlToPdf(
@@ -218,8 +226,7 @@ public class EmlToPdf {
return "attachment_" + filename.hashCode() + "_" + System.nanoTime();
}
private static String convertEmlToHtmlBasic(
byte[] emlBytes, EmlToPdfRequest request) {
private static String convertEmlToHtmlBasic(byte[] emlBytes, EmlToPdfRequest request) {
if (emlBytes == null || emlBytes.length == 0) {
throw new IllegalArgumentException("EML file is empty or null");
}
@@ -335,7 +342,6 @@ public class EmlToPdf {
Object message =
mimeMessageConstructor.newInstance(session, new ByteArrayInputStream(emlBytes));
return extractEmailContentAdvanced(message, request);
} catch (ReflectiveOperationException e) {
@@ -346,8 +352,7 @@ public class EmlToPdf {
}
}
private static String convertEmlToHtmlAdvanced(
byte[] emlBytes, EmlToPdfRequest request) {
private static String convertEmlToHtmlAdvanced(byte[] emlBytes, EmlToPdfRequest request) {
EmailContent content = extractEmailContentAdvanced(emlBytes, request);
return generateEnhancedEmailHtml(content, request);
}
@@ -479,8 +484,12 @@ public class EmlToPdf {
// Create attachment info with paperclip emoji before filename
attachmentInfo
.append("<div class=\"attachment-item\">")
.append("<span class=\"attachment-icon\">").append(MimeConstants.ATTACHMENT_ICON_PLACEHOLDER).append("</span> ")
.append("<span class=\"attachment-name\">").append(escapeHtml(filename)).append("</span>");
.append("<span class=\"attachment-icon\">")
.append(MimeConstants.ATTACHMENT_ICON_PLACEHOLDER)
.append("</span> ")
.append("<span class=\"attachment-name\">")
.append(escapeHtml(filename))
.append("</span>");
// Add content type and encoding info
if (!contentType.isEmpty() || !encoding.isEmpty()) {
@@ -503,17 +512,20 @@ public class EmlToPdf {
String content = new String(emlBytes, 0, checkLength, StandardCharsets.UTF_8);
String lowerContent = content.toLowerCase();
boolean hasFrom = lowerContent.contains("from:") || lowerContent.contains("return-path:");
boolean hasFrom =
lowerContent.contains("from:") || lowerContent.contains("return-path:");
boolean hasSubject = lowerContent.contains("subject:");
boolean hasMessageId = lowerContent.contains("message-id:");
boolean hasDate = lowerContent.contains("date:");
boolean hasTo = lowerContent.contains("to:")
|| lowerContent.contains("cc:")
|| lowerContent.contains("bcc:");
boolean hasMimeStructure = lowerContent.contains("multipart/")
|| lowerContent.contains("text/plain")
|| lowerContent.contains("text/html")
|| lowerContent.contains("boundary=");
boolean hasTo =
lowerContent.contains("to:")
|| lowerContent.contains("cc:")
|| lowerContent.contains("bcc:");
boolean hasMimeStructure =
lowerContent.contains("multipart/")
|| lowerContent.contains("text/plain")
|| lowerContent.contains("text/html")
|| lowerContent.contains("boundary=");
int headerCount = 0;
if (hasFrom) headerCount++;
@@ -684,17 +696,19 @@ public class EmlToPdf {
html.append(" font-size: ").append(fontSize - 1).append("px;\n");
html.append("}\n\n");
html.append(".email-body {\n");
html.append(" word-wrap: break-word;\n");
html.append("}\n\n");
html.append(".attachment-section {\n");
html.append(" margin-top: 15px;\n");
html.append(" padding: 10px;\n");
html.append(" background-color: ").append(StyleConstants.ATTACHMENT_BACKGROUND_COLOR).append(";\n");
html.append(" border: 1px solid ").append(StyleConstants.ATTACHMENT_BORDER_COLOR).append(";\n");
html.append(" background-color: ")
.append(StyleConstants.ATTACHMENT_BACKGROUND_COLOR)
.append(";\n");
html.append(" border: 1px solid ")
.append(StyleConstants.ATTACHMENT_BORDER_COLOR)
.append(";\n");
html.append(" border-radius: 3px;\n");
html.append("}\n\n");
html.append(".attachment-section h3 {\n");
@@ -746,7 +760,6 @@ public class EmlToPdf {
html.append(" margin-left: 8px;\n");
html.append("}\n\n");
// Basic image styling: ensure images are responsive but not overly constrained.
html.append("img {\n");
html.append(" max-width: 100%;\n"); // Make images responsive to container width
@@ -801,7 +814,9 @@ public class EmlToPdf {
java.lang.reflect.Method getAllRecipients = messageClass.getMethod("getAllRecipients");
Object[] recipients = (Object[]) getAllRecipients.invoke(message);
content.setTo(
recipients != null && recipients.length > 0 ? safeMimeDecode(recipients[0].toString()) : "");
recipients != null && recipients.length > 0
? safeMimeDecode(recipients[0].toString())
: "");
java.lang.reflect.Method getSentDate = messageClass.getMethod("getSentDate");
content.setDate((Date) getSentDate.invoke(message));
@@ -908,13 +923,14 @@ public class EmlToPdf {
try {
attachmentData = inputStream.readAllBytes();
} catch (IOException e) {
log.warn("Failed to read InputStream attachment: {}", e.getMessage());
log.warn(
"Failed to read InputStream attachment: {}",
e.getMessage());
}
} else if (attachmentContent instanceof byte[] byteArray) {
attachmentData = byteArray;
} else if (attachmentContent instanceof String stringContent) {
attachmentData =
stringContent.getBytes(StandardCharsets.UTF_8);
attachmentData = stringContent.getBytes(StandardCharsets.UTF_8);
}
if (attachmentData != null) {
@@ -974,7 +990,9 @@ public class EmlToPdf {
html.append("<div><strong>From:</strong> ")
.append(escapeHtml(content.getFrom()))
.append("</div>\n");
html.append("<div><strong>To:</strong> ").append(escapeHtml(content.getTo())).append("</div>\n");
html.append("<div><strong>To:</strong> ")
.append(escapeHtml(content.getTo()))
.append("</div>\n");
if (content.getDate() != null) {
html.append("<div><strong>Date:</strong> ")
@@ -1014,15 +1032,20 @@ public class EmlToPdf {
? attachment.getEmbeddedFilename()
: attachment.getFilename());
html.append("<div class=\"attachment-item\" id=\"").append(uniqueId).append("\">")
.append("<span class=\"attachment-icon\">").append(MimeConstants.PAPERCLIP_EMOJI).append("</span> ")
html.append("<div class=\"attachment-item\" id=\"")
.append(uniqueId)
.append("\">")
.append("<span class=\"attachment-icon\">")
.append(MimeConstants.PAPERCLIP_EMOJI)
.append("</span> ")
.append("<span class=\"attachment-name\">")
.append(escapeHtml(safeMimeDecode(attachment.getFilename())))
.append("</span>");
String sizeStr = formatFileSize(attachment.getSizeBytes());
html.append(" <span class=\"attachment-details\">(").append(sizeStr);
if (attachment.getContentType() != null && !attachment.getContentType().isEmpty()) {
if (attachment.getContentType() != null
&& !attachment.getContentType().isEmpty()) {
html.append(", ").append(escapeHtml(attachment.getContentType()));
}
html.append(")</span></div>\n");
@@ -1031,8 +1054,7 @@ public class EmlToPdf {
if (request.isIncludeAttachments()) {
html.append("<div class=\"attachment-info-note\">\n");
html.append(
"<p><em>Attachments are embedded in the file.</em></p>\n");
html.append("<p><em>Attachments are embedded in the file.</em></p>\n");
html.append("</div>\n");
} else {
html.append("<div class=\"attachment-info-note\">\n");
@@ -1050,7 +1072,10 @@ public class EmlToPdf {
return html.toString();
}
private static byte[] attachFilesToPdf(byte[] pdfBytes, List<EmailAttachment> attachments, stirling.software.common.service.CustomPDFDocumentFactory pdfDocumentFactory)
private static byte[] attachFilesToPdf(
byte[] pdfBytes,
List<EmailAttachment> attachments,
stirling.software.common.service.CustomPDFDocumentFactory pdfDocumentFactory)
throws IOException {
try (PDDocument document = pdfDocumentFactory.load(pdfBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
@@ -1104,7 +1129,8 @@ public class EmlToPdf {
// Create embedded file
PDEmbeddedFile embeddedFile =
new PDEmbeddedFile(document, new ByteArrayInputStream(attachment.getData()));
new PDEmbeddedFile(
document, new ByteArrayInputStream(attachment.getData()));
embeddedFile.setSize(attachment.getData().length);
embeddedFile.setCreationDate(new GregorianCalendar());
if (attachment.getContentType() != null) {
@@ -1150,11 +1176,13 @@ public class EmlToPdf {
}
}
private static String getUniqueFilename(String filename, List<String> embeddedFiles, Map<String, PDComplexFileSpecification> efMap) {
private static String getUniqueFilename(
String filename,
List<String> embeddedFiles,
Map<String, PDComplexFileSpecification> efMap) {
String uniqueFilename = filename;
int counter = 1;
while (embeddedFiles.contains(uniqueFilename)
|| efMap.containsKey(uniqueFilename)) {
while (embeddedFiles.contains(uniqueFilename) || efMap.containsKey(uniqueFilename)) {
String extension = "";
String baseName = filename;
int lastDot = filename.lastIndexOf('.');
@@ -1203,8 +1231,8 @@ public class EmlToPdf {
}
private static void addAttachmentAnnotationToPage(
PDDocument document, PDPage page, EmailAttachment attachment, float x, float y)
throws IOException {
PDDocument document, PDPage page, EmailAttachment attachment, float x, float y)
throws IOException {
PDAnnotationFileAttachment fileAnnotation = new PDAnnotationFileAttachment();
@@ -1226,11 +1254,12 @@ public class EmlToPdf {
// Set invisibility flags but keep it functional
fileAnnotation.setInvisible(true);
fileAnnotation.setHidden(false); // Must be false to remain clickable
fileAnnotation.setNoView(false); // Must be false to remain clickable
fileAnnotation.setHidden(false); // Must be false to remain clickable
fileAnnotation.setNoView(false); // Must be false to remain clickable
fileAnnotation.setPrinted(false);
PDEmbeddedFilesNameTreeNode efTree = document.getDocumentCatalog().getNames().getEmbeddedFiles();
PDEmbeddedFilesNameTreeNode efTree =
document.getDocumentCatalog().getNames().getEmbeddedFiles();
if (efTree != null) {
Map<String, PDComplexFileSpecification> efMap = efTree.getNames();
if (efMap != null) {
@@ -1246,24 +1275,27 @@ public class EmlToPdf {
page.getAnnotations().add(fileAnnotation);
log.info("Added attachment annotation for '{}' on page {}",
attachment.getFilename(), document.getPages().indexOf(page) + 1);
log.info(
"Added attachment annotation for '{}' on page {}",
attachment.getFilename(),
document.getPages().indexOf(page) + 1);
}
private static @NotNull PDRectangle getPdRectangle(PDPage page, float x, float y) {
PDRectangle mediaBox = page.getMediaBox();
float pdfY = mediaBox.getHeight() - y;
float iconWidth = StyleConstants.ATTACHMENT_ICON_WIDTH; // Keep original size for clickability
float iconHeight = StyleConstants.ATTACHMENT_ICON_HEIGHT; // Keep original size for clickability
float iconWidth =
StyleConstants.ATTACHMENT_ICON_WIDTH; // Keep original size for clickability
float iconHeight =
StyleConstants.ATTACHMENT_ICON_HEIGHT; // Keep original size for clickability
// Keep the full-size rectangle so it remains clickable
return new PDRectangle(
x + StyleConstants.ANNOTATION_X_OFFSET,
pdfY - iconHeight + StyleConstants.ANNOTATION_Y_OFFSET,
iconWidth,
iconHeight
);
x + StyleConstants.ANNOTATION_X_OFFSET,
pdfY - iconHeight + StyleConstants.ANNOTATION_Y_OFFSET,
iconWidth,
iconHeight);
}
private static String formatEmailDate(Date date) {
@@ -1293,23 +1325,27 @@ public class EmlToPdf {
COSDictionary catalogDict = catalog.getCOSObject();
// Set PageMode to UseAttachments - this is the standard PDF specification approach
// PageMode values: UseNone, UseOutlines, UseThumbs, FullScreen, UseOC, UseAttachments
// PageMode values: UseNone, UseOutlines, UseThumbs, FullScreen, UseOC,
// UseAttachments
catalogDict.setName(COSName.PAGE_MODE, "UseAttachments");
// Also set viewer preferences for better attachment viewing experience
COSDictionary viewerPrefs = (COSDictionary) catalogDict.getDictionaryObject(COSName.VIEWER_PREFERENCES);
COSDictionary viewerPrefs =
(COSDictionary) catalogDict.getDictionaryObject(COSName.VIEWER_PREFERENCES);
if (viewerPrefs == null) {
viewerPrefs = new COSDictionary();
catalogDict.setItem(COSName.VIEWER_PREFERENCES, viewerPrefs);
}
// Set NonFullScreenPageMode to UseAttachments as fallback for viewers that support it
// Set NonFullScreenPageMode to UseAttachments as fallback for viewers that support
// it
viewerPrefs.setName(COSName.getPDFName("NonFullScreenPageMode"), "UseAttachments");
// Additional viewer preferences that may help with attachment display
viewerPrefs.setBoolean(COSName.getPDFName("DisplayDocTitle"), true);
log.info("Set PDF PageMode to UseAttachments to automatically show attachments pane");
log.info(
"Set PDF PageMode to UseAttachments to automatically show attachments pane");
}
} catch (Exception e) {
// Log warning but don't fail the entire operation for viewer preferences
@@ -1391,7 +1427,7 @@ public class EmlToPdf {
}
}
case '_' -> // In RFC 2047, underscore represents space
result.append(' ');
result.append(' ');
default -> result.append(c);
}
}
@@ -1464,8 +1500,7 @@ public class EmlToPdf {
private float y;
private String character;
public EmojiPosition() {
}
public EmojiPosition() {}
public EmojiPosition(int pageIndex, float x, float y, String character) {
this.pageIndex = pageIndex;
@@ -1475,9 +1510,8 @@ public class EmlToPdf {
}
}
public static class EmojiPositionFinder extends org.apache.pdfbox.text.PDFTextStripper {
@Getter
private final List<EmojiPosition> positions = new ArrayList<>();
public static class EmojiPositionFinder extends org.apache.pdfbox.text.PDFTextStripper {
@Getter private final List<EmojiPosition> positions = new ArrayList<>();
private int currentPageIndex;
private boolean sortByPosition;
private boolean isInAttachmentSection;
@@ -1503,7 +1537,9 @@ public class EmlToPdf {
}
@Override
protected void writeString(String string, List<org.apache.pdfbox.text.TextPosition> textPositions) throws IOException {
protected void writeString(
String string, List<org.apache.pdfbox.text.TextPosition> textPositions)
throws IOException {
// Check if we are entering or exiting the attachment section
String lowerString = string.toLowerCase();
@@ -1513,10 +1549,14 @@ public class EmlToPdf {
attachmentSectionFound = true;
}
// Look for attachment section end markers (common patterns that indicate end of attachments)
if (isInAttachmentSection && (lowerString.contains("</body>") ||
lowerString.contains("</html>") ||
(attachmentSectionFound && lowerString.trim().isEmpty() && string.length() > 50))) {
// Look for attachment section end markers (common patterns that indicate end of
// attachments)
if (isInAttachmentSection
&& (lowerString.contains("</body>")
|| lowerString.contains("</html>")
|| (attachmentSectionFound
&& lowerString.trim().isEmpty()
&& string.length() > 50))) {
isInAttachmentSection = false;
}
@@ -1527,17 +1567,17 @@ public class EmlToPdf {
for (int i = 0; i < string.length(); i++) {
// Check if we have a complete paperclip emoji at this position
if (i < string.length() - 1 &&
string.substring(i, i + 2).equals(paperclipEmoji) &&
i < textPositions.size()) {
if (i < string.length() - 1
&& string.substring(i, i + 2).equals(paperclipEmoji)
&& i < textPositions.size()) {
org.apache.pdfbox.text.TextPosition textPosition = textPositions.get(i);
EmojiPosition position = new EmojiPosition(
currentPageIndex,
textPosition.getXDirAdj(),
textPosition.getYDirAdj(),
paperclipEmoji
);
EmojiPosition position =
new EmojiPosition(
currentPageIndex,
textPosition.getXDirAdj(),
textPosition.getYDirAdj(),
paperclipEmoji);
positions.add(position);
}
}
@@ -1554,7 +1594,6 @@ public class EmlToPdf {
return sortByPosition;
}
public void reset() {
positions.clear();
currentPageIndex = 0;
@@ -2,7 +2,6 @@ package stirling.software.common.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springframework.ui.Model;
import org.springframework.web.servlet.ModelAndView;
@@ -11,13 +11,10 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.configuration.RuntimePathConfig;
@Component
@@ -1,5 +1,6 @@
package stirling.software.common.util;
import io.github.pixee.security.ZipSecurity;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
@@ -13,9 +14,6 @@ import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import io.github.pixee.security.ZipSecurity;
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
@@ -1,5 +1,8 @@
package stirling.software.common.util;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import io.github.pixee.security.HostValidator;
import io.github.pixee.security.Urls;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -14,19 +17,11 @@ import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.web.multipart.MultipartFile;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import io.github.pixee.security.HostValidator;
import io.github.pixee.security.Urls;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.configuration.InstallationPathConfig;
@Slf4j
@@ -1,22 +1,18 @@
package stirling.software.common.util;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import org.springframework.web.multipart.MultipartFile;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifSubIFDDirectory;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
@Slf4j
public class ImageProcessingUtils {
@@ -1,5 +1,8 @@
package stirling.software.common.util;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.util.data.MutableDataSet;
import io.github.pixee.security.Filenames;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -12,22 +15,14 @@ import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.util.data.MutableDataSet;
import io.github.pixee.security.Filenames;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
@Slf4j
@@ -1,5 +1,6 @@
package stirling.software.common.util;
import io.github.pixee.security.Filenames;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
@@ -10,10 +11,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.imageio.*;
import javax.imageio.stream.ImageOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
@@ -30,11 +30,6 @@ import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.service.CustomPDFDocumentFactory;
@Slf4j
@@ -1,5 +1,6 @@
package stirling.software.common.util;
import io.github.pixee.security.BoundedLineReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -12,11 +13,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import io.github.pixee.security.BoundedLineReader;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.ApplicationProperties;
@Slf4j
@@ -1,10 +1,9 @@
package stirling.software.common.util;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.ServerSocket;
import jakarta.servlet.http.HttpServletRequest;
public class UrlUtils {
public static String getOrigin(HttpServletRequest request) {
@@ -1,10 +1,10 @@
package stirling.software.common.util;
import io.github.pixee.security.Filenames;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -12,8 +12,6 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
public class WebResponseUtils {
public static ResponseEntity<byte[]> boasToWebResponse(
@@ -13,7 +13,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;
import org.snakeyaml.engine.v2.api.Dump;
import org.snakeyaml.engine.v2.api.DumpSettings;
import org.snakeyaml.engine.v2.api.LoadSettings;
@@ -30,8 +30,6 @@ import org.snakeyaml.engine.v2.nodes.Tag;
import org.snakeyaml.engine.v2.parser.ParserImpl;
import org.snakeyaml.engine.v2.scanner.StreamReader;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class YamlHelper {
@@ -8,7 +8,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
@@ -21,9 +21,6 @@ import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.apache.pdfbox.text.TextPosition;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.api.misc.HighContrastColorCombination;
import stirling.software.common.model.api.misc.ReplaceAndInvert;
@@ -7,9 +7,7 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import javax.imageio.ImageIO;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
@@ -18,7 +16,6 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.common.model.api.misc.ReplaceAndInvert;
public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
@@ -3,7 +3,6 @@ package stirling.software.common.util.misc;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.text.PDFTextStripperByArea;
import org.apache.pdfbox.text.TextPosition;
@@ -1,13 +1,10 @@
package stirling.software.common.util.misc;
import java.io.IOException;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.common.model.api.PDFFile;
import stirling.software.common.model.api.misc.ReplaceAndInvert;
@@ -1,15 +1,12 @@
package stirling.software.common.util.propertyeditor;
import java.beans.PropertyEditorSupport;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.beans.PropertyEditorSupport;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.api.security.RedactionArea;
@Slf4j
@@ -1,11 +1,10 @@
package stirling.software.common.util.propertyeditor;
import java.beans.PropertyEditorSupport;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.beans.PropertyEditorSupport;
import java.util.HashMap;
import java.util.Map;
public class StringToMapPropertyEditor extends PropertyEditorSupport {