provideHtmlTestCases() {
return Stream.of(
- Arguments.of(
- "This is valid HTML with formatting .
",
- new String[] {"", "", ""}
- ),
- Arguments.of(
- "Text with bold , italic , underline , "
- + "emphasis , strong , strikethrough , "
- + "strike , subscript , superscript , "
- + "teletype , code
, big , small .
",
- new String[] {"bold ", "italic ", "emphasis ", "strong "}
- ),
- Arguments.of(
- "Division
Heading 1 Heading 2 Heading 3 "
- + "Heading 4 Heading 5 Heading 6 "
- + "Blockquote "
- + "Ordered item ",
- new String[] {"", "
", "", "", "", "", ""}
- )
- );
+ Arguments.of(
+ "This is valid HTML with formatting .
",
+ new String[] {"", "", ""}),
+ Arguments.of(
+ "Text with bold , italic , underline , "
+ + "emphasis , strong , strikethrough , "
+ + "strike , subscript , superscript , "
+ + "teletype , code
, big , small .
",
+ new String[] {
+ "bold ",
+ "italic ",
+ "emphasis ",
+ "strong "
+ }),
+ Arguments.of(
+ "Division
Heading 1 Heading 2 Heading 3 "
+ + "Heading 4 Heading 5 Heading 6 "
+ + "Blockquote "
+ + "Ordered item ",
+ new String[] {
+ "", "
", "", "", "", "", ""
+ }));
}
@Test
diff --git a/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java b/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java
index 7960128df..38b5e9277 100644
--- a/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java
+++ b/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java
@@ -1,6 +1,5 @@
package stirling.software.SPDF.utils;
-import io.github.pixee.security.ZipSecurity;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -29,6 +28,8 @@ import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
+import io.github.pixee.security.ZipSecurity;
+
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
/**
@@ -214,7 +215,8 @@ class PDFToFileTest {
// Verify the content by unzipping it
try (ZipInputStream zipStream =
- ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) {
+ ZipSecurity.createHardenedInputStream(
+ new java.io.ByteArrayInputStream(response.getBody()))) {
ZipEntry entry;
boolean foundMdFiles = false;
boolean foundImage = false;
@@ -286,7 +288,8 @@ class PDFToFileTest {
// Verify the content by unzipping it
try (ZipInputStream zipStream =
- ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) {
+ ZipSecurity.createHardenedInputStream(
+ new java.io.ByteArrayInputStream(response.getBody()))) {
ZipEntry entry;
boolean foundMainHtml = false;
boolean foundIndexHtml = false;
@@ -437,7 +440,8 @@ class PDFToFileTest {
// Verify the content by unzipping it
try (ZipInputStream zipStream =
- ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) {
+ ZipSecurity.createHardenedInputStream(
+ new java.io.ByteArrayInputStream(response.getBody()))) {
ZipEntry entry;
boolean foundMainFile = false;
boolean foundMediaFiles = false;
From c8e25f4c5ad4be4c97c6bf797cd4e766e4fe0a43 Mon Sep 17 00:00:00 2001
From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
Date: Tue, 20 May 2025 12:02:26 +0100
Subject: [PATCH 026/195] Fix TemplateResolver and LibreOfficeListener bugs
(#3555)
## Summary
- log missing exceptions in FileFallbackTemplateResolver
- implement exists check for InputStreamTemplateResource
- use LISTENER_PORT constant when verifying LibreOffice listener
## Testing
- `./gradlew build --no-daemon`
- `./gradlew test --no-daemon`
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../java/stirling/software/SPDF/LibreOfficeListener.java | 3 ++-
.../software/SPDF/config/FileFallbackTemplateResolver.java | 6 +++++-
.../software/SPDF/model/InputStreamTemplateResource.java | 3 +--
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/main/java/stirling/software/SPDF/LibreOfficeListener.java b/src/main/java/stirling/software/SPDF/LibreOfficeListener.java
index 5b00700e8..2be2a082c 100644
--- a/src/main/java/stirling/software/SPDF/LibreOfficeListener.java
+++ b/src/main/java/stirling/software/SPDF/LibreOfficeListener.java
@@ -31,7 +31,8 @@ public class LibreOfficeListener {
log.info("waiting for listener to start");
try (Socket socket = new Socket()) {
socket.connect(
- new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second
+ new InetSocketAddress("localhost", LISTENER_PORT),
+ 1000); // Timeout after 1 second
return true;
} catch (Exception e) {
return false;
diff --git a/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java b/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java
index b6315db92..8073f2358 100644
--- a/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java
+++ b/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java
@@ -11,8 +11,11 @@ import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
import org.thymeleaf.templateresource.FileTemplateResource;
import org.thymeleaf.templateresource.ITemplateResource;
+import lombok.extern.slf4j.Slf4j;
+
import stirling.software.SPDF.model.InputStreamTemplateResource;
+@Slf4j
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
private final ResourceLoader resourceLoader;
@@ -40,7 +43,8 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
}
} catch (IOException e) {
-
+ // Log the exception to help with debugging issues loading external templates
+ log.warn("Unable to read template '{}' from file system", resourceName, e);
}
InputStream inputStream =
diff --git a/src/main/java/stirling/software/SPDF/model/InputStreamTemplateResource.java b/src/main/java/stirling/software/SPDF/model/InputStreamTemplateResource.java
index b4271df02..3e0bd65e8 100644
--- a/src/main/java/stirling/software/SPDF/model/InputStreamTemplateResource.java
+++ b/src/main/java/stirling/software/SPDF/model/InputStreamTemplateResource.java
@@ -39,7 +39,6 @@ public class InputStreamTemplateResource implements ITemplateResource {
@Override
public boolean exists() {
- // TODO Auto-generated method stub
- return false;
+ return inputStream != null;
}
}
From 46cc2e05df97cef04118ea1fde880b52f3d919ba Mon Sep 17 00:00:00 2001
From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
Date: Tue, 20 May 2025 12:05:18 +0100
Subject: [PATCH 027/195] Add additional unit tests for utils and EE (#3557)
## Summary
- add tests for LicenseKeyChecker
- expand GeneralUtils coverage
- cover extra PdfUtils functionality
- merge PdfUtilsMoreTest into PdfUtilsTest
## Testing
- `./gradlew test --no-daemon`
- `./gradlew build spotlessApply --no-daemon`
---
.../SPDF/EE/LicenseKeyCheckerTest.java | 77 +++++++++++++++++++
.../utils/GeneralUtilsAdditionalTest.java | 41 ++++++++++
.../software/SPDF/utils/PdfUtilsTest.java | 73 ++++++++++++++++++
3 files changed, 191 insertions(+)
create mode 100644 src/test/java/stirling/software/SPDF/EE/LicenseKeyCheckerTest.java
create mode 100644 src/test/java/stirling/software/SPDF/utils/GeneralUtilsAdditionalTest.java
diff --git a/src/test/java/stirling/software/SPDF/EE/LicenseKeyCheckerTest.java b/src/test/java/stirling/software/SPDF/EE/LicenseKeyCheckerTest.java
new file mode 100644
index 000000000..90754ee04
--- /dev/null
+++ b/src/test/java/stirling/software/SPDF/EE/LicenseKeyCheckerTest.java
@@ -0,0 +1,77 @@
+package stirling.software.SPDF.EE;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
+import stirling.software.SPDF.model.ApplicationProperties;
+
+@ExtendWith(MockitoExtension.class)
+class LicenseKeyCheckerTest {
+
+ @Mock private KeygenLicenseVerifier verifier;
+
+ @Test
+ void premiumDisabled_skipsVerification() {
+ ApplicationProperties props = new ApplicationProperties();
+ props.getPremium().setEnabled(false);
+ props.getPremium().setKey("dummy");
+
+ LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
+
+ assertEquals(License.NORMAL, checker.getPremiumLicenseEnabledResult());
+ verifyNoInteractions(verifier);
+ }
+
+ @Test
+ void directKey_verified() {
+ ApplicationProperties props = new ApplicationProperties();
+ props.getPremium().setEnabled(true);
+ props.getPremium().setKey("abc");
+ when(verifier.verifyLicense("abc")).thenReturn(License.PRO);
+
+ LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
+
+ assertEquals(License.PRO, checker.getPremiumLicenseEnabledResult());
+ verify(verifier).verifyLicense("abc");
+ }
+
+ @Test
+ void fileKey_verified(@TempDir Path temp) throws IOException {
+ Path file = temp.resolve("license.txt");
+ Files.writeString(file, "filekey");
+
+ ApplicationProperties props = new ApplicationProperties();
+ props.getPremium().setEnabled(true);
+ props.getPremium().setKey("file:" + file.toString());
+ when(verifier.verifyLicense("filekey")).thenReturn(License.ENTERPRISE);
+
+ LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
+
+ assertEquals(License.ENTERPRISE, checker.getPremiumLicenseEnabledResult());
+ verify(verifier).verifyLicense("filekey");
+ }
+
+ @Test
+ void missingFile_resultsNormal(@TempDir Path temp) {
+ Path file = temp.resolve("missing.txt");
+ ApplicationProperties props = new ApplicationProperties();
+ props.getPremium().setEnabled(true);
+ props.getPremium().setKey("file:" + file.toString());
+
+ LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
+
+ assertEquals(License.NORMAL, checker.getPremiumLicenseEnabledResult());
+ verifyNoInteractions(verifier);
+ }
+}
diff --git a/src/test/java/stirling/software/SPDF/utils/GeneralUtilsAdditionalTest.java b/src/test/java/stirling/software/SPDF/utils/GeneralUtilsAdditionalTest.java
new file mode 100644
index 000000000..4a48cdb0f
--- /dev/null
+++ b/src/test/java/stirling/software/SPDF/utils/GeneralUtilsAdditionalTest.java
@@ -0,0 +1,41 @@
+package stirling.software.SPDF.utils;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class GeneralUtilsAdditionalTest {
+
+ @Test
+ void testConvertSizeToBytes() {
+ assertEquals(1024L, GeneralUtils.convertSizeToBytes("1KB"));
+ assertEquals(1024L * 1024, GeneralUtils.convertSizeToBytes("1MB"));
+ assertEquals(1024L * 1024 * 1024, GeneralUtils.convertSizeToBytes("1GB"));
+ assertEquals(100L * 1024 * 1024, GeneralUtils.convertSizeToBytes("100"));
+ assertNull(GeneralUtils.convertSizeToBytes("invalid"));
+ assertNull(GeneralUtils.convertSizeToBytes(null));
+ }
+
+ @Test
+ void testFormatBytes() {
+ assertEquals("512 B", GeneralUtils.formatBytes(512));
+ assertEquals("1.00 KB", GeneralUtils.formatBytes(1024));
+ assertEquals("1.00 MB", GeneralUtils.formatBytes(1024L * 1024));
+ assertEquals("1.00 GB", GeneralUtils.formatBytes(1024L * 1024 * 1024));
+ }
+
+ @Test
+ void testURLHelpersAndUUID() {
+ assertTrue(GeneralUtils.isValidURL("https://example.com"));
+ assertFalse(GeneralUtils.isValidURL("htp:/bad"));
+ assertFalse(GeneralUtils.isURLReachable("http://localhost"));
+ assertFalse(GeneralUtils.isURLReachable("ftp://example.com"));
+
+ assertTrue(GeneralUtils.isValidUUID("123e4567-e89b-12d3-a456-426614174000"));
+ assertFalse(GeneralUtils.isValidUUID("not-a-uuid"));
+
+ assertFalse(GeneralUtils.isVersionHigher(null, "1.0"));
+ assertTrue(GeneralUtils.isVersionHigher("2.0", "1.9"));
+ assertFalse(GeneralUtils.isVersionHigher("1.0", "1.0.1"));
+ }
+}
diff --git a/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java
index a03564ee9..b8994faee 100644
--- a/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java
+++ b/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java
@@ -5,12 +5,17 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
@@ -18,6 +23,10 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
+import stirling.software.SPDF.model.ApplicationProperties;
+import stirling.software.SPDF.service.CustomPDFDocumentFactory;
+import stirling.software.SPDF.service.PdfMetadataService;
+
public class PdfUtilsTest {
@Test
@@ -49,4 +58,68 @@ public class PdfUtilsTest {
assertTrue(PdfUtils.hasImagesOnPage(page));
}
+
+ @Test
+ void testPageCountComparators() throws Exception {
+ PDDocument doc1 = new PDDocument();
+ doc1.addPage(new PDPage());
+ doc1.addPage(new PDPage());
+ doc1.addPage(new PDPage());
+ PdfUtils utils = new PdfUtils();
+ assertTrue(utils.pageCount(doc1, 2, "greater"));
+
+ PDDocument doc2 = new PDDocument();
+ doc2.addPage(new PDPage());
+ doc2.addPage(new PDPage());
+ doc2.addPage(new PDPage());
+ assertTrue(utils.pageCount(doc2, 3, "equal"));
+
+ PDDocument doc3 = new PDDocument();
+ doc3.addPage(new PDPage());
+ doc3.addPage(new PDPage());
+ assertTrue(utils.pageCount(doc3, 5, "less"));
+
+ PDDocument doc4 = new PDDocument();
+ doc4.addPage(new PDPage());
+ assertThrows(IllegalArgumentException.class, () -> utils.pageCount(doc4, 1, "bad"));
+ }
+
+ @Test
+ void testPageSize() throws Exception {
+ PDDocument doc = new PDDocument();
+ PDPage page = new PDPage(PDRectangle.A4);
+ doc.addPage(page);
+ PDRectangle rect = page.getMediaBox();
+ String expected = rect.getWidth() + "x" + rect.getHeight();
+ PdfUtils utils = new PdfUtils();
+ assertTrue(utils.pageSize(doc, expected));
+ }
+
+ @Test
+ void testOverlayImage() throws Exception {
+ PDDocument doc = new PDDocument();
+ doc.addPage(new PDPage(PDRectangle.A4));
+ ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
+ doc.save(pdfOut);
+ doc.close();
+
+ BufferedImage image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+ Graphics2D g = image.createGraphics();
+ g.setColor(Color.RED);
+ g.fillRect(0, 0, 10, 10);
+ g.dispose();
+ ByteArrayOutputStream imgOut = new ByteArrayOutputStream();
+ javax.imageio.ImageIO.write(image, "png", imgOut);
+
+ PdfMetadataService meta =
+ new PdfMetadataService(new ApplicationProperties(), "label", false, null);
+ CustomPDFDocumentFactory factory = new CustomPDFDocumentFactory(meta);
+
+ byte[] result =
+ PdfUtils.overlayImage(
+ factory, pdfOut.toByteArray(), imgOut.toByteArray(), 0, 0, false);
+ try (PDDocument resultDoc = factory.load(result)) {
+ assertEquals(1, resultDoc.getNumberOfPages());
+ }
+ }
}
From bef86b44e48e7f8a7e1dc1f83faf3e17dc624aef Mon Sep 17 00:00:00 2001
From: "stirlingbot[bot]" <195170888+stirlingbot[bot]@users.noreply.github.com>
Date: Tue, 20 May 2025 12:07:03 +0100
Subject: [PATCH 028/195] Update 3rd Party Licenses (#3559)
Auto-generated by StirlingBot
Signed-off-by: stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
---
src/main/resources/static/3rdPartyLicenses.json | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/main/resources/static/3rdPartyLicenses.json b/src/main/resources/static/3rdPartyLicenses.json
index e8f6942a9..b701c302f 100644
--- a/src/main/resources/static/3rdPartyLicenses.json
+++ b/src/main/resources/static/3rdPartyLicenses.json
@@ -553,7 +553,7 @@
{
"moduleName": "io.micrometer:micrometer-core",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
- "moduleVersion": "1.14.7",
+ "moduleVersion": "1.15.0",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -1637,7 +1637,7 @@
{
"moduleName": "org.springframework.security:spring-security-saml2-service-provider",
"moduleUrl": "https://spring.io/projects/spring-security",
- "moduleVersion": "6.4.5",
+ "moduleVersion": "6.5.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
@@ -1714,7 +1714,7 @@
{
"moduleName": "org.springframework:spring-jdbc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
- "moduleVersion": "6.2.6",
+ "moduleVersion": "6.2.7",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
@@ -1742,7 +1742,7 @@
{
"moduleName": "org.springframework:spring-webmvc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
- "moduleVersion": "6.2.6",
+ "moduleVersion": "6.2.7",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
From 70349fb7e32551b0afa9ba8c5ae3d85bfcc30e48 Mon Sep 17 00:00:00 2001
From: Reece Browne <74901996+reecebrowne@users.noreply.github.com>
Date: Tue, 20 May 2025 12:08:20 +0100
Subject: [PATCH 029/195] remove legacy homepage (#3518)
# 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.
---
.../controller/web/HomeWebController.java | 5 +-
src/main/resources/static/css/home-legacy.css | 229 --------
src/main/resources/static/js/favourites.js | 6 +-
.../resources/static/js/homecard-legacy.js | 266 ---------
src/main/resources/static/js/pages/home.js | 4 -
.../fragments/featureGroupHeaderLegacy.html | 6 -
src/main/resources/templates/home-legacy.html | 528 ------------------
src/main/resources/templates/home.html | 7 -
8 files changed, 3 insertions(+), 1048 deletions(-)
delete mode 100644 src/main/resources/static/css/home-legacy.css
delete mode 100644 src/main/resources/static/js/homecard-legacy.js
delete mode 100644 src/main/resources/templates/fragments/featureGroupHeaderLegacy.html
delete mode 100644 src/main/resources/templates/home-legacy.html
diff --git a/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java b/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java
index 9a3b2b3e2..9fc644863 100644
--- a/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java
+++ b/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java
@@ -77,9 +77,8 @@ public class HomeWebController {
}
@GetMapping("/home-legacy")
- public String homeLegacy(Model model) {
- model.addAttribute("currentPage", "home-legacy");
- return "home-legacy";
+ public String redirectHomeLegacy() {
+ return "redirect:/";
}
@GetMapping(value = "/robots.txt", produces = MediaType.TEXT_PLAIN_VALUE)
diff --git a/src/main/resources/static/css/home-legacy.css b/src/main/resources/static/css/home-legacy.css
deleted file mode 100644
index b25fafc17..000000000
--- a/src/main/resources/static/css/home-legacy.css
+++ /dev/null
@@ -1,229 +0,0 @@
-#searchBar {
- color: var(--md-sys-color-on-surface);
- background-color: var(--md-sys-color-surface-container-low);
- width: 100%;
- font-size: 16px;
- margin-bottom: 2rem;
- padding: 0.75rem 3.5rem;
- border: 1px solid var(--md-sys-color-outline-variant);
- border-radius: 3rem;
- outline-color: var(--md-sys-color-outline-variant);
- }
-
- #filtersContainer {
- display: flex;
- width: 100%;
- align-items: center;
- justify-content: center;
- gap: 10px;
- }
-
- .filter-button {
- color: var(--md-sys-color-secondary);
- user-select: none;
- cursor: pointer;
- transition: transform 0.3s;
- transform-origin: center center;
- }
-
- .filter-button:hover {
- transform: scale(1.08);
- }
-
- .search-icon {
- position: absolute;
- margin: 0.75rem 1rem;
- border: 0.1rem solid transparent;
- }
-
- .features-container {
- display: flex;
- flex-direction: column;
- gap: 30px;
- }
-
- .feature-group-legacy {
- display: flex;
- flex-direction: column;
- }
-
- .feature-group-header {
- display: flex;
- align-items: center;
- justify-content: flex-start;
- color: var(--md-sys-color-on-surface);
- margin-bottom: 15px;
- user-select: none;
- cursor: pointer;
- gap: 10px;
- }
-
- .feature-group-container {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr));
- gap: 30px 30px;
- overflow: hidden;
- margin: -20px;
- padding: 20px;
- box-sizing:content-box;
- }
-
- .feature-group-container.animated-group {
- transition: 0.5s all;
- }
-
- .feature-group-legacy.collapsed>.feature-group-container {
- max-height: 0 !important;
- margin: 0;
- padding: 0;
- }
-
- .header-expand-button {
- transition: 0.5s all;
- transform: rotate(90deg);
- }
-
- .header-expand-button.collapsed {
- transform: rotate(0deg);
- }
-
- .feature-card {
- border: 1px solid var(--md-sys-color-surface-5);
- border-radius: 1.75rem;
- padding: 1.25rem;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- background: var(--md-sys-color-surface-5);
- transition:
- transform 0.3s,
- border 0.3s;
- transform-origin: center center;
- outline: 0px solid transparent;
- position:relative;
- }
-
- .feature-card a {
- text-decoration: none;
- color: var(--md-sys-color-on-surface);
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 100%;
- }
-
- .feature-card .card-text {
- font-size: .875rem;
- }
-
- .feature-card:hover {
- cursor: pointer;
- transform: scale(1.08);
- box-shadow: var(--md-sys-elevation-2);
- }
-
- .card-title.text-primary {
- color: #000;
- }
-
- .home-card-icon {
- width: 3rem;
- height: 3rem;
- transform: translateY(-5px);
- }
-
- .favorite-icon {
- display: none !important;
- position: absolute;
- top: 10px;
- right: 10px;
- color: var(--md-sys-color-secondary);
- }
-
- #tool-icon {
- height: 100%;
- }
-
- #tool-text {
- margin: 0.0rem 0 0 1.25rem;
- }
-
- .card-title {
- margin-bottom: 1rem;
- font-size: 1.1rem;
- }
-
- /* Only show the favorite icons when the parent card is being hovered over */
- .feature-card:hover .favorite-icon {
- display: block !important;
- }
-
- .favorite-icon img {
- filter: brightness(0) invert(var(--md-theme-filter-color));
- }
-
- .favorite-icon:hover .material-symbols-rounded {
- transform: scale(1.2);
- }
-
- .favorite-icon .material-symbols-rounded.fill{
- color: #f5c000;
- }
-
- .jumbotron {
- padding: 3rem 3rem;
- /* Reduce vertical padding */
- }
-
- .lookatme {
- opacity: 1;
- position: relative;
- display: inline-block;
- }
-
- .lookatme::after {
- color: #e33100;
- text-shadow: 0 0 5px #e33100;
- /* in the html, the data-lookatme-text attribute must */
- /* contain the same text as the .lookatme element */
- content: attr(data-lookatme-text);
- padding: inherit;
- position: absolute;
- inset: 0 0 0 0;
- z-index: 1;
- /* 20 steps / 2 seconds = 10fps */
- -webkit-animation: 2s infinite Pulse steps(20);
- animation: 2s infinite Pulse steps(20);
- }
-
- @keyframes Pulse {
- from {
- opacity: 0;
- }
-
- 50% {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- }
- }
-
- .update-notice {
- animation: scale 1s infinite alternate;
- }
-
- @keyframes scale {
- 0% {
- transform: scale(0.96);
- }
-
- 100% {
- transform: scale(1);
- }
- }
-
- .hidden {
- visibility: hidden;
- }
diff --git a/src/main/resources/static/js/favourites.js b/src/main/resources/static/js/favourites.js
index 913c656b2..5aab52824 100644
--- a/src/main/resources/static/js/favourites.js
+++ b/src/main/resources/static/js/favourites.js
@@ -126,11 +126,7 @@ function addToFavorites(entryId) {
localStorage.setItem('favoritesList', JSON.stringify(favoritesList));
updateFavoritesDropdown();
updateFavoriteIcons();
- const currentPath = window.location.pathname;
- if (currentPath.includes('home-legacy')) {
- syncFavoritesLegacy();
- } else {
+
initializeCards();
- }
}
}
diff --git a/src/main/resources/static/js/homecard-legacy.js b/src/main/resources/static/js/homecard-legacy.js
deleted file mode 100644
index a43453f1c..000000000
--- a/src/main/resources/static/js/homecard-legacy.js
+++ /dev/null
@@ -1,266 +0,0 @@
-function filterCardsLegacy() {
- var input = document.getElementById('searchBar');
- var filter = input.value.toUpperCase();
-
- let featureGroups = document.querySelectorAll('.feature-group-legacy');
- const collapsedGroups = getCollapsedGroups();
-
- for (const featureGroup of featureGroups) {
- var cards = featureGroup.querySelectorAll('.feature-card');
-
- let groupMatchesFilter = false;
- for (var i = 0; i < cards.length; i++) {
- var card = cards[i];
- var title = card.querySelector('h5.card-title').innerText;
- var text = card.querySelector('p.card-text').innerText;
-
- // Get the navbar tags associated with the card
- var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`);
- var navbarTags = navbarItem ? navbarItem.getAttribute('data-bs-tags') : '';
-
- var content = title + ' ' + text + ' ' + navbarTags;
-
- if (content.toUpperCase().indexOf(filter) > -1) {
- card.style.display = '';
- groupMatchesFilter = true;
- } else {
- card.style.display = 'none';
- }
- }
-
- if (!groupMatchesFilter) {
- featureGroup.style.display = 'none';
- } else {
- featureGroup.style.display = '';
- resetOrTemporarilyExpandGroup(featureGroup, filter, collapsedGroups);
- }
- }
-}
-
-function getCollapsedGroups() {
- return localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : [];
-}
-
-function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = '', collapsedGroups = []) {
- const shouldResetCollapse = filterKeywords.trim() === '';
- if (shouldResetCollapse) {
- // Resetting the group's expand/collapse to its original state (as in collapsed groups)
- const isCollapsed = collapsedGroups.indexOf(featureGroup.id) != -1;
- expandCollapseToggle(featureGroup, !isCollapsed);
- } else {
- // Temporarily expands feature group without affecting the actual/stored collapsed groups
- featureGroup.classList.remove('collapsed');
- featureGroup.querySelector('.header-expand-button').classList.remove('collapsed');
- }
-}
-
-function updateFavoritesSectionLegacy() {
- const favoritesContainer = document.getElementById('groupFavorites').querySelector('.feature-group-container');
- favoritesContainer.innerHTML = '';
- const cards = Array.from(document.querySelectorAll('.feature-card:not(.duplicate)'));
- const addedCardIds = new Set();
- let favoritesAmount = 0;
-
- cards.forEach((card) => {
- const favouritesList = JSON.parse(localStorage.getItem('favoritesList') || '[]');
-
- if (favouritesList.includes(card.id) && !addedCardIds.has(card.id)) {
- const duplicate = card.cloneNode(true);
- duplicate.classList.add('duplicate');
- favoritesContainer.appendChild(duplicate);
- addedCardIds.add(card.id);
- favoritesAmount++;
- }
- });
-
- if (favoritesAmount === 0) {
- document.getElementById('groupFavorites').style.display = 'none';
- } else {
- document.getElementById('groupFavorites').style.display = 'flex';
- }
- reorderCards(favoritesContainer);
-}
-
-function syncFavoritesLegacy() {
- const cards = Array.from(document.querySelectorAll('.feature-card'));
- cards.forEach((card) => {
- const isFavorite = localStorage.getItem(card.id) === 'favorite';
- const starIcon = card.querySelector('.favorite-icon span.material-symbols-rounded');
- if (starIcon) {
- if (isFavorite) {
- starIcon.classList.remove('no-fill');
- starIcon.classList.add('fill');
- card.classList.add('favorite');
- } else {
- starIcon.classList.remove('fill');
- starIcon.classList.add('no-fill');
- card.classList.remove('favorite');
- }
- }
- });
- updateFavoritesSectionLegacy();
- updateFavoritesDropdown();
- filterCardsLegacy();
-}
-
-function reorderCards(container) {
- var cards = Array.from(container.querySelectorAll('.feature-card'));
- cards.forEach(function (card) {
- container.removeChild(card);
- });
- cards.sort(function (a, b) {
- var aIsFavorite = localStorage.getItem(a.id) === 'favorite';
- var bIsFavorite = localStorage.getItem(b.id) === 'favorite';
- if (a.id === 'update-link') {
- return -1;
- }
- if (b.id === 'update-link') {
- return 1;
- }
-
- if (aIsFavorite && !bIsFavorite) {
- return -1;
- } else if (!aIsFavorite && bIsFavorite) {
- return 1;
- } else {
- return a.id > b.id;
- }
- });
- cards.forEach(function (card) {
- container.appendChild(card);
- });
-}
-
-function reorderAllCards() {
- const containers = Array.from(document.querySelectorAll('.feature-group-container'));
- containers.forEach(function (container) {
- reorderCards(container);
- });
-}
-
-function initializeCardsLegacy() {
- reorderAllCards();
- updateFavoritesSectionLegacy();
- updateFavoritesDropdown();
- filterCardsLegacy();
-}
-
-function showFavoritesOnly() {
- const groups = Array.from(document.querySelectorAll('.feature-group-legacy'));
- if (localStorage.getItem('favoritesOnly') === 'true') {
- groups.forEach((group) => {
- if (group.id !== 'groupFavorites') {
- group.style.display = 'none';
- }
- });
- } else {
- groups.forEach((group) => {
- if (group.id !== 'groupFavorites') {
- group.style.display = 'flex';
- }
- });
- }
-}
-
-function toggleFavoritesOnly() {
- if (localStorage.getItem('favoritesOnly') === 'true') {
- localStorage.setItem('favoritesOnly', 'false');
- } else {
- localStorage.setItem('favoritesOnly', 'true');
- }
- showFavoritesOnly();
-}
-
-// Expands a feature group on true, collapses it on false and toggles state on null.
-function expandCollapseToggle(group, expand = null) {
- if (expand === null) {
- group.classList.toggle('collapsed');
- group.querySelector('.header-expand-button').classList.toggle('collapsed');
- } else if (expand) {
- group.classList.remove('collapsed');
- group.querySelector('.header-expand-button').classList.remove('collapsed');
- } else {
- group.classList.add('collapsed');
- group.querySelector('.header-expand-button').classList.add('collapsed');
- }
-
- const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : [];
- const groupIndex = collapsed.indexOf(group.id);
-
- if (group.classList.contains('collapsed')) {
- if (groupIndex === -1) {
- collapsed.push(group.id);
- }
- } else {
- if (groupIndex !== -1) {
- collapsed.splice(groupIndex, 1);
- }
- }
-
- localStorage.setItem('collapsedGroups', JSON.stringify(collapsed));
-}
-
-function expandCollapseAll(expandAll) {
- const groups = Array.from(document.querySelectorAll('.feature-group-legacy'));
- groups.forEach((group) => {
- expandCollapseToggle(group, expandAll);
- });
-}
-
-window.onload = function () {
- initializeCardsLegacy();
- syncFavoritesLegacy(); // Ensure everything is in sync on page load
-};
-
-document.addEventListener('DOMContentLoaded', function () {
- const materialIcons = new FontFaceObserver('Material Symbols Rounded');
-
- materialIcons
- .load()
- .then(() => {
- document.querySelectorAll('.feature-card.hidden').forEach((el) => {
- el.classList.remove('hidden');
- });
- })
- .catch(() => {
- console.error('Material Symbols Rounded font failed to load.');
- });
-
- Array.from(document.querySelectorAll('.feature-group-header-legacy')).forEach((header) => {
- const parent = header.parentNode;
- const container = header.parentNode.querySelector('.feature-group-container');
- if (parent.id !== 'groupFavorites') {
- // container.style.maxHeight = container.scrollHeight + 'px';
- }
- header.onclick = () => {
- expandCollapseToggle(parent);
- };
- });
-
- const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : [];
- const groupsArray = Array.from(document.querySelectorAll('.feature-group-legacy'));
-
- groupsArray.forEach((group) => {
- if (collapsed.indexOf(group.id) !== -1) {
- expandCollapseToggle(group, false);
- }
- });
-
- // Necessary in order to not fire the transition animation on page load, which looks wrong.
- // The timeout isn't doing anything visible to the user, so it's not making the page load look slower.
- setTimeout(() => {
- groupsArray.forEach((group) => {
- const container = group.querySelector('.feature-group-container');
- container.classList.add('animated-group');
- });
- }, 500);
-
- Array.from(document.querySelectorAll('.feature-group-header')).forEach((header) => {
- const parent = header.parentNode;
- header.onclick = () => {
- expandCollapseToggle(parent);
- };
- });
-
- showFavoritesOnly();
-});
diff --git a/src/main/resources/static/js/pages/home.js b/src/main/resources/static/js/pages/home.js
index bb1e1ad4a..d474e9439 100644
--- a/src/main/resources/static/js/pages/home.js
+++ b/src/main/resources/static/js/pages/home.js
@@ -55,10 +55,6 @@ hideCookieBanner();
updateFavoriteIcons();
const contentPath = /*[[${@contextPath}]]*/ '';
-const defaultView = localStorage.getItem('defaultView') || 'home'; // Default to "home"
-if (defaultView === 'home-legacy') {
- window.location.href = contentPath + 'home-legacy'; // Redirect to legacy view
-}
document.addEventListener('DOMContentLoaded', function () {
const surveyVersion = '3.0';
diff --git a/src/main/resources/templates/fragments/featureGroupHeaderLegacy.html b/src/main/resources/templates/fragments/featureGroupHeaderLegacy.html
deleted file mode 100644
index 0a8f7e9b1..000000000
--- a/src/main/resources/templates/fragments/featureGroupHeaderLegacy.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
\ No newline at end of file
diff --git a/src/main/resources/templates/home-legacy.html b/src/main/resources/templates/home-legacy.html
deleted file mode 100644
index d60ac220e..000000000
--- a/src/main/resources/templates/home-legacy.html
+++ /dev/null
@@ -1,528 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- search
-
-
-
-
-
- star
-
-
- expand_all
-
-
- collapse_all
-
-
- dashboard
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
If you're using Stirling PDF at work, we'd love to speak to you. We're offering free technical support in exchange for a 15 minute user discovery session.
-
This is a chance to:
-
🛠️ Get help with deployment, integrations, or troubleshooting
-
📢 Provide direct feedback on performance, edge cases, and feature gaps
-
🔍 Help us refine Stirling PDF for real-world enterprise use
-
If you're interested, you can book time with our team directly.
-
Looking forward to digging into your use cases and making Stirling PDF even better!
-
Book meeting
-
-
-
Not a business and/or interested in a meeting?
-
-
Please consider taking our survey!
-
Take Survey
-
-
-
-
-
-
-
-
-
-
-
-
-
Stirling PDF has opt in analytics to help us improve the product. We do
- not track any personal information or file contents.
-
Please consider enabling analytics to help Stirling-PDF grow and to allow
- us to understand our users better.
-
You can change the settings for analytics in the config/settings.yml file
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html
index a7cbbbd80..0bb9a2c06 100644
--- a/src/main/resources/templates/home.html
+++ b/src/main/resources/templates/home.html
@@ -82,13 +82,6 @@
visibility
-
-
- home
-
-
From 8bfdb2abb5609e2fe30226e2915017cd9a949b00 Mon Sep 17 00:00:00 2001
From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
Date: Tue, 20 May 2025 17:42:42 +0100
Subject: [PATCH 030/195] Update home.html (#3560)
# 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.
---
src/main/resources/templates/home.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html
index 0bb9a2c06..26597ecd7 100644
--- a/src/main/resources/templates/home.html
+++ b/src/main/resources/templates/home.html
@@ -138,7 +138,7 @@
🔍 Help us refine Stirling PDF for real-world enterprise use
If you're interested, you can book time with our team directly.
Looking forward to digging into your use cases and making Stirling PDF even better!
- Book meeting
+ Book meeting
Not a business and/or interested in a meeting?
@@ -232,4 +232,4 @@