Compare commits

..

8 Commits

Author SHA1 Message Date
dependabot[bot]
a503adf24a
Bump com.opencsv:opencsv from 5.11 to 5.11.1
Bumps com.opencsv:opencsv from 5.11 to 5.11.1.

---
updated-dependencies:
- dependency-name: com.opencsv:opencsv
  dependency-version: 5.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 11:52:34 +00:00
Dario Ghunney Ware
c7d6a063d7
Multi module refactor (#3640)
# Description of Changes

Migrated Stirling PDF to a multi-module structure:

* Introduced new `:stirling-pdf` module
* Moved all the core logic and features of Stirling PDF into
`:stirling-pdf`
* Updated paths of jobs and scripts

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-06-09 12:51:41 +01:00
ConnorYoh
baaaa5a0b2
Disabled auto capture for posthog (#3661)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-06-09 12:02:56 +01:00
stirlingbot[bot]
461f98be3e
🌐 Sync Translations + Update README Progress Table (#3657)
### Description of Changes

This Pull Request was automatically generated to synchronize updates to
translation files and documentation. Below are the details of the
changes made:

#### **1. Synchronization of Translation Files**
- Updated translation files (`messages_*.properties`) to reflect changes
in the reference file `messages_en_GB.properties`.
- Ensured consistency and synchronization across all supported language
files.
- Highlighted any missing or incomplete translations.

#### **2. Update README.md**
- Generated the translation progress table in `README.md`.
- Added a summary of the current translation status for all supported
languages.
- Included up-to-date statistics on translation coverage.

#### **Why these changes are necessary**
- Keeps translation files aligned with the latest reference updates.
- Ensures the documentation reflects the current translation progress.

---

Auto-generated by [create-pull-request][1].

[1]: https://github.com/peter-evans/create-pull-request

---------

Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-06-08 21:43:15 +01:00
Balázs Szücs
9fbb0325b5
Add EML to PDF conversion functionality (#3650)
This pull request introduces a new feature for converting EML (email)
files to PDF format, along with various customization options. It
includes backend support for the conversion process, frontend
integration for user interaction, and updates to localization and
navigation.

### Backend Changes:
* **Added EML to PDF Conversion Logic**: Implemented a new controller
`ConvertEmlToPDF` with an endpoint `/api/v1/convert/eml/pdf` to handle
EML-to-PDF conversion requests. This includes validation, support for
HTML intermediate files, and enhanced options such as attachment
handling and size limits.
(`src/main/java/stirling/software/SPDF/controller/api/converters/ConvertEmlToPDF.java`)
* **New Model for Conversion Requests**: Introduced `EmlToPdfRequest`
class to encapsulate request parameters like attachment inclusion,
maximum attachment size, and HTML download options.
(`common/src/main/java/stirling/software/common/model/api/converters/EmlToPdfRequest.java`)
* **Dependency Update**: Added `jakarta.mail:jakarta.mail-api:2.1.3` to
the project dependencies for handling EML files. (`common/build.gradle`)


![image](https://github.com/user-attachments/assets/34c5755a-d58d-4fc6-8a51-e83ac9f4afae)


### Frontend Changes:
* **New Web Form**: Created a new HTML page `eml-to-pdf.html` for the
EML-to-PDF conversion tool, allowing users to upload EML files and
configure options.
(`src/main/resources/templates/convert/eml-to-pdf.html`)
* **Navigation Update**: Added a navigation entry for the EML-to-PDF
tool in the sidebar.
(`src/main/resources/templates/fragments/navElements.html`)


![image](https://github.com/user-attachments/assets/afbd929d-7745-4d52-8aeb-a88d21662ca6)


### Localization and UI Enhancements:
* **Localization Strings**: Added support for the EML-to-PDF tool in the
`messages_en_GB.properties` file, including titles, descriptions, and
help texts. (`src/main/resources/messages_en_GB.properties`)
* **Web Controller Update**: Added a new route `/eml-to-pdf` in
`ConverterWebController` to serve the EML-to-PDF form.
(`src/main/java/stirling/software/SPDF/controller/web/ConverterWebController.java`)

### Highlights:
* Attachment support: and Attachment section is created with fully
working PDFAnnotations, which enable users to click paperclip and
redirects to the attachment. (Requires PDF.js)
*  If attachments are present creates a catalog of attachments
* Encoding support inside the body and header for local charachters e.g:
ö,ő,ü etc..
* Optional: Users can download HTMLs, aswell as PDFs
* Advanced features for conversion that: keep links, keep as much
formatting as possible, keep images incl relative sizes, popular fonts
and many more.

### Known limitations
* Generally EML-to-HTML is very reliable however emails with complicated
layout cause problem for Weasyprint, so not all emails can reliably
converted to PDF.
* Users need PDF.js and PDFCatalog support for best attachment/embedding
support (but is not strict requirement)

### Challanges
* Embedding was a large headache, not the Embedding itself per se more
so the additional niceties such as: links, the catalog, consistent
symbols (replaced the paperclip that is generated by pdf viewer with
emoji paperclip that is consistent for everybody) and it was generally
prone all sorts of hard to diagnose issues.
* Encoding issues
* Formatting issues

However I think addressed these so shouldn't cause any additional
headache. :)

### Examples:


![image](https://github.com/user-attachments/assets/9b560216-984d-4b9f-9ae7-8975723c894d)


![image](https://github.com/user-attachments/assets/98c7a67d-82d4-4f5a-bf42-8ebc4be18b42)


![image](https://github.com/user-attachments/assets/30a53fc9-9636-4090-b5b0-0866cc054c6c)


![image](https://github.com/user-attachments/assets/80c2d109-5259-4d3f-b97a-00b513d547e9)



Closes #503

---

## 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)
- [x] 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)

- [x] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-08 21:26:01 +01:00
stirlingbot[bot]
47ac4a4730
🌐 Sync Translations + Update README Progress Table (#3644)
### Description of Changes

This Pull Request was automatically generated to synchronize updates to
translation files and documentation. Below are the details of the
changes made:

#### **1. Synchronization of Translation Files**
- Updated translation files (`messages_*.properties`) to reflect changes
in the reference file `messages_en_GB.properties`.
- Ensured consistency and synchronization across all supported language
files.
- Highlighted any missing or incomplete translations.

#### **2. Update README.md**
- Generated the translation progress table in `README.md`.
- Added a summary of the current translation status for all supported
languages.
- Included up-to-date statistics on translation coverage.

#### **Why these changes are necessary**
- Keeps translation files aligned with the latest reference updates.
- Ensures the documentation reflects the current translation progress.

---

Auto-generated by [create-pull-request][1].

[1]: https://github.com/peter-evans/create-pull-request

Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-06-05 16:53:36 +01:00
Balázs Szücs
2d5091c8e3
Updated Hungarian translation for new features: fake-scan, get-info, and for some misc front-end updates. (#3646)
# Changes 

Updated the hungarian translation for get-info, fake-scan and misc.

---

## 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)
- [x] 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)

- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-05 16:52:08 +01:00
stirlingbot[bot]
b78d05e8ee
Update 3rd Party Licenses (#3647)
Auto-generated by stirlingbot[bot]

Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com>
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-06-05 16:51:16 +01:00
924 changed files with 5072 additions and 7686 deletions

14
.gitattributes vendored
View File

@ -1,10 +1,10 @@
* text=auto eol=lf * text=auto eol=lf
# Ignore all JavaScript files in a directory # Ignore all JavaScript files in a directory
src/main/resources/static/pdfjs/* linguist-vendored stirling-pdf/src/main/resources/static/pdfjs/* linguist-vendored
src/main/resources/static/pdfjs/** linguist-vendored stirling-pdf/src/main/resources/static/pdfjs/** linguist-vendored
src/main/resources/static/pdfjs-legacy/* linguist-vendored stirling-pdf/src/main/resources/static/pdfjs-legacy/* linguist-vendored
src/main/resources/static/pdfjs-legacy/** linguist-vendored stirling-pdf/src/main/resources/static/pdfjs-legacy/** linguist-vendored
src/main/resources/static/css/bootstrap-icons.css linguist-vendored stirling-pdf/src/main/resources/static/css/bootstrap-icons.css linguist-vendored
src/main/resources/static/css/bootstrap.min.css linguist-vendored stirling-pdf/src/main/resources/static/css/bootstrap.min.css linguist-vendored
src/main/resources/static/css/fonts/* linguist-vendored stirling-pdf/src/main/resources/static/css/fonts/* linguist-vendored

View File

@ -1,60 +1,45 @@
Translation: Translation:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/messages_*_*.properties'
- any-glob-to-any-file: 'scripts/ignore_translation.toml' - any-glob-to-any-file: 'scripts/ignore_translation.toml'
- any-glob-to-any-file: 'src/main/resources/templates/fragments/languages.html' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/templates/fragments/languages.html'
Front End: Front End:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/resources/templates/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/templates/**/*'
- any-glob-to-any-file: 'src/main/resources/static/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/static/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/**' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/**'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/UI/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/UI/**/*'
Java: Java:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/**/*.java' - any-glob-to-any-file: 'common/src/main/java/**/*.java'
- any-glob-to-any-file: 'proprietary/src/main/java/**/*.java'
- any-glob-to-any-file: 'stirling-pdf/src/main/java/**/*.java'
Back End: Back End:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/config/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/**/*'
- any-glob-to-any-file: 'src/main/resources/settings.yml.template' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/settings.yml.template'
- any-glob-to-any-file: 'src/main/resources/application.properties' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/application.properties'
- any-glob-to-any-file: 'src/main/resources/banner.txt' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/banner.txt'
- any-glob-to-any-file: 'scripts/png_to_webp.py' - any-glob-to-any-file: 'scripts/png_to_webp.py'
- any-glob-to-any-file: 'split_photos.py' - any-glob-to-any-file: 'split_photos.py'
Security: Security:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/interfaces/DatabaseInterface.java' - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/DatabaseController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/EmailController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/H2SQLController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/DatabaseWebController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/UserController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/api/Email.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/exception/BackupNotFoundException.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/exception/NoProviderFoundExceptionjava'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/provider/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AuthenticationType.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/ApiKeyAuthenticationToken.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AttemptCounter.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/Authority.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/PersistentLogin.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/SessionEntity.java'
- any-glob-to-any-file: 'scripts/download-security-jar.sh' - any-glob-to-any-file: 'scripts/download-security-jar.sh'
- any-glob-to-any-file: '.github/workflows/dependency-review.yml' - any-glob-to-any-file: '.github/workflows/dependency-review.yml'
- any-glob-to-any-file: '.github/workflows/scorecards.yml' - any-glob-to-any-file: '.github/workflows/scorecards.yml'
API: API:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/OpenApiConfig.java' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/MetricsController.java' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/MetricsController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/api/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/model/api/**/*'
- any-glob-to-any-file: 'scripts/png_to_webp.py' - any-glob-to-any-file: 'scripts/png_to_webp.py'
- any-glob-to-any-file: 'split_photos.py' - any-glob-to-any-file: 'split_photos.py'
- any-glob-to-any-file: '.github/workflows/swagger.yml' - any-glob-to-any-file: '.github/workflows/swagger.yml'
@ -88,7 +73,9 @@ Devtools:
Test: Test:
- changed-files: - changed-files:
- any-glob-to-any-file: 'cucumber/**/*' - any-glob-to-any-file: 'cucumber/**/*'
- any-glob-to-any-file: 'src/test/**/*' - any-glob-to-any-file: 'common/src/test/**/*'
- any-glob-to-any-file: 'proprietary/src/test/**/*'
- any-glob-to-any-file: 'stirling-pdf/src/test/**/*'
- any-glob-to-any-file: 'src/testing/**/*' - any-glob-to-any-file: 'src/testing/**/*'
- any-glob-to-any-file: '.pre-commit-config' - any-glob-to-any-file: '.pre-commit-config'
- any-glob-to-any-file: '.github/workflows/pre_commit.yml' - any-glob-to-any-file: '.github/workflows/pre_commit.yml'

View File

@ -317,7 +317,7 @@ def check_for_differences(reference_file, file_list, branch, actor):
report.append("## ❌ Overall Check Status: **_Failed_**") report.append("## ❌ Overall Check Status: **_Failed_**")
report.append("") report.append("")
report.append( report.append(
f"@{actor} please check your translation if it conforms to the standard. Follow the format of [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)" f"@{actor} please check your translation if it conforms to the standard. Follow the format of [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/stirling-pdf/src/main/resources/messages_en_GB.properties)"
) )
else: else:
report.append("## ✅ Overall Check Status: **_Success_**") report.append("## ✅ Overall Check Status: **_Success_**")

View File

@ -4,7 +4,7 @@ on:
pull_request_target: pull_request_target:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
paths: paths:
- "src/main/resources/messages_*.properties" - "stirling-pdf/src/main/resources/messages_*.properties"
permissions: permissions:
contents: read # Allow read access to repository content contents: read # Allow read access to repository content
@ -61,7 +61,20 @@ jobs:
run: | run: |
echo "Fetching PR changed files..." echo "Fetching PR changed files..."
echo "Getting list of changed files from PR..." echo "Getting list of changed files from PR..."
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$' > changed_files.txt # Filter only matching property files # Check if PR number exists
if [ -z "${{ steps.get-pr-data.outputs.pr_number }}" ]; then
echo "Error: PR number is empty"
exit 1
fi
# Get changed files and filter for properties files, handle case where no matches are found
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^stirling-pdf/src/main/resources/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$' > changed_files.txt || echo "No matching properties files found in PR"
# Check if any files were found
if [ ! -s changed_files.txt ]; then
echo "No properties files changed in this PR"
echo "Workflow will exit early as no relevant files to check"
exit 0
fi
echo "Found $(wc -l < changed_files.txt) matching properties files"
- name: Determine reference file test - name: Determine reference file test
id: determine-file id: determine-file
@ -103,7 +116,7 @@ jobs:
// Filter for relevant files based on the PR changes // Filter for relevant files based on the PR changes
const changedFiles = files const changedFiles = files
.map(file => file.filename) .map(file => file.filename)
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file)); .filter(file => /^stirling-pdf\src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
console.log("Changed files:", changedFiles); console.log("Changed files:", changedFiles);
@ -141,12 +154,12 @@ jobs:
// Determine reference file // Determine reference file
let referenceFilePath; let referenceFilePath;
if (changedFiles.includes("src/main/resources/messages_en_GB.properties")) { if (changedFiles.includes("stirling-pdf/src/main/resources/messages_en_GB.properties")) {
console.log("Using PR branch reference file."); console.log("Using PR branch reference file.");
const { data: fileContent } = await github.rest.repos.getContent({ const { data: fileContent } = await github.rest.repos.getContent({
owner: prRepoOwner, owner: prRepoOwner,
repo: prRepoName, repo: prRepoName,
path: "src/main/resources/messages_en_GB.properties", path: "stirling-pdf/src/main/resources/messages_en_GB.properties",
ref: branch, ref: branch,
}); });
@ -158,7 +171,7 @@ jobs:
const { data: fileContent } = await github.rest.repos.getContent({ const { data: fileContent } = await github.rest.repos.getContent({
owner: repoOwner, owner: repoOwner,
repo: repoName, repo: repoName,
path: "src/main/resources/messages_en_GB.properties", path: "stirling-pdf/src/main/resources/messages_en_GB.properties",
ref: "main", ref: "main",
}); });

View File

@ -57,11 +57,11 @@ jobs:
- name: Move and rename license file - name: Move and rename license file
run: | run: |
mv build/reports/dependency-license/index.json src/main/resources/static/3rdPartyLicenses.json mv build/reports/dependency-license/index.json stirling-pdf/src/main/resources/static/3rdPartyLicenses.json
- name: Commit changes - name: Commit changes
run: | run: |
git add src/main/resources/static/3rdPartyLicenses.json git add stirling-pdf/src/main/resources/static/3rdPartyLicenses.json
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
- name: Create Pull Request - name: Create Pull Request

View File

@ -8,8 +8,8 @@ on:
paths: paths:
- "build.gradle" - "build.gradle"
- "README.md" - "README.md"
- "src/main/resources/messages_*.properties" - "stirling-pdf/src/main/resources/messages_*.properties"
- "src/main/resources/static/3rdPartyLicenses.json" - "stirling-pdf/src/main/resources/static/3rdPartyLicenses.json"
- "scripts/ignore_translation.toml" - "scripts/ignore_translation.toml"
permissions: permissions:
@ -41,11 +41,11 @@ jobs:
- name: Sync translation property files - name: Sync translation property files
run: | run: |
python .github/scripts/check_language_properties.py --reference-file "src/main/resources/messages_en_GB.properties" --branch main python .github/scripts/check_language_properties.py --reference-file "stirling-pdf/src/main/resources/messages_en_GB.properties" --branch main
- name: Commit translation files - name: Commit translation files
run: | run: |
git add src/main/resources/messages_*.properties git add stirling-pdf/src/main/resources/messages_*.properties
git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "No changes detected" git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "No changes detected"
- name: Install dependencies - name: Install dependencies
@ -101,4 +101,4 @@ jobs:
sign-commits: true sign-commits: true
add-paths: | add-paths: |
README.md README.md
src/main/resources/messages_*.properties stirling-pdf/src/main/resources/messages_*.properties

4
.gitignore vendored
View File

@ -125,6 +125,9 @@ SwaggerDoc.json
*.rar *.rar
*.db *.db
/build /build
/stirling-pdf/build
/common/build
/proprietary/build
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
@ -194,4 +197,3 @@ id_ed25519.pub
# node_modules # node_modules
node_modules/ node_modules/
*.mjs

View File

@ -20,7 +20,7 @@ repos:
- --skip="./.*,*.csv,*.json,*.ambr" - --skip="./.*,*.csv,*.json,*.ambr"
- --quiet-level=2 - --quiet-level=2
files: \.(html|css|js|py|md)$ files: \.(html|css|js|py|md)$
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js) exclude: (.vscode|.devcontainer|stirling-pdf/src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
- repo: https://github.com/gitleaks/gitleaks - repo: https://github.com/gitleaks/gitleaks
rev: v8.26.0 rev: v8.26.0
hooks: hooks:

View File

@ -137,9 +137,9 @@ services:
ports: ports:
- "8080:8080" - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - ./stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - ./stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw - ./stirling/latest/logs:/logs:rw
environment: environment:
DISABLE_ADDITIONAL_FEATURES: "false" DISABLE_ADDITIONAL_FEATURES: "false"
SECURITY_ENABLELOGIN: "true" SECURITY_ENABLELOGIN: "true"
@ -332,7 +332,7 @@ Thymeleaf is a server-side Java HTML template engine. It is used in Stirling-PDF
### Thymeleaf overview ### Thymeleaf overview
In Stirling-PDF, Thymeleaf is used to create HTML templates that are rendered on the server side. These templates are located in the `src/main/resources/templates` directory. Thymeleaf templates use a combination of HTML and special Thymeleaf attributes to dynamically generate content. In Stirling-PDF, Thymeleaf is used to create HTML templates that are rendered on the server side. These templates are located in the `stirling-pdf/src/main/resources/templates` directory. Thymeleaf templates use a combination of HTML and special Thymeleaf attributes to dynamically generate content.
Some examples of this are: Some examples of this are:
@ -384,7 +384,7 @@ This would generate n entries of tr for each person in exampleData
### Adding a New Feature to the Backend (API) ### Adding a New Feature to the Backend (API)
1. **Create a New Controller:** 1. **Create a New Controller:**
- Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/api` directory. - Create a new Java class in the `stirling-pdf/src/main/java/stirling/software/SPDF/controller/api` directory.
- Annotate the class with `@RestController` and `@RequestMapping` to define the API endpoint. - Annotate the class with `@RestController` and `@RequestMapping` to define the API endpoint.
- Ensure to add API documentation annotations like `@Tag(name = "General", description = "General APIs")` and `@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")`. - Ensure to add API documentation annotations like `@Tag(name = "General", description = "General APIs")` and `@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")`.
@ -411,7 +411,7 @@ This would generate n entries of tr for each person in exampleData
``` ```
2. **Define the Service Layer:** (Not required but often useful) 2. **Define the Service Layer:** (Not required but often useful)
- Create a new service class in the `src/main/java/stirling/software/SPDF/service` directory. - Create a new service class in the `stirling-pdf/src/main/java/stirling/software/SPDF/service` directory.
- Implement the business logic for the new feature. - Implement the business logic for the new feature.
```java ```java
@ -463,7 +463,7 @@ This would generate n entries of tr for each person in exampleData
### Adding a New Feature to the Frontend (UI) ### Adding a New Feature to the Frontend (UI)
1. **Create a New Thymeleaf Template:** 1. **Create a New Thymeleaf Template:**
- Create a new HTML file in the `src/main/resources/templates` directory. - Create a new HTML file in the `stirling-pdf/src/main/resources/templates` directory.
- Use Thymeleaf attributes to dynamically generate content. - Use Thymeleaf attributes to dynamically generate content.
- Use `extract-page.html` as a base example for the HTML template, which is useful to ensure importing of the general layout, navbar, and footer. - Use `extract-page.html` as a base example for the HTML template, which is useful to ensure importing of the general layout, navbar, and footer.
@ -507,7 +507,7 @@ This would generate n entries of tr for each person in exampleData
``` ```
2. **Create a New Controller for the UI:** 2. **Create a New Controller for the UI:**
- Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/ui` directory. - Create a new Java class in the `stirling-pdf/src/main/java/stirling/software/SPDF/controller/ui` directory.
- Annotate the class with `@Controller` and `@RequestMapping` to define the UI endpoint. - Annotate the class with `@Controller` and `@RequestMapping` to define the UI endpoint.
```java ```java
@ -537,7 +537,7 @@ This would generate n entries of tr for each person in exampleData
3. **Update the Navigation Bar:** 3. **Update the Navigation Bar:**
- Add a link to the new feature page in the navigation bar. - Add a link to the new feature page in the navigation bar.
- Update the `src/main/resources/templates/fragments/navbar.html` file. - Update the `stirling-pdf/src/main/resources/templates/fragments/navbar.html` file.
```html ```html
<li class="nav-item"> <li class="nav-item">
@ -551,7 +551,7 @@ When adding a new feature or modifying existing ones in Stirling-PDF, you'll nee
### 1. Locate Existing Language Files ### 1. Locate Existing Language Files
Find the existing `messages.properties` files in the `src/main/resources` directory. You'll see files like: Find the existing `messages.properties` files in the `stirling-pdf/src/main/resources` directory. You'll see files like:
- `messages.properties` (default, usually English) - `messages.properties` (default, usually English)
- `messages_en_GB.properties` - `messages_en_GB.properties`

View File

@ -4,7 +4,7 @@ FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be02
# Copy necessary files # Copy necessary files
COPY scripts /scripts COPY scripts /scripts
COPY pipeline /pipeline COPY pipeline /pipeline
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/ COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
#COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/ #COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
COPY build/libs/*.jar app.jar COPY build/libs/*.jar app.jar

View File

@ -26,7 +26,7 @@ FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be02
# Copy necessary files # Copy necessary files
COPY scripts /scripts COPY scripts /scripts
COPY pipeline /pipeline COPY pipeline /pipeline
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/ COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
COPY --from=build /app/build/libs/*.jar app.jar COPY --from=build /app/build/libs/*.jar app.jar
ARG VERSION_TAG ARG VERSION_TAG

View File

@ -10,7 +10,7 @@ Fork Stirling-PDF and create a new branch out of `main`.
Then add a reference to the language in the navbar by adding a new language entry to the dropdown: Then add a reference to the language in the navbar by adding a new language entry to the dropdown:
- Edit the file: [languages.html](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html) - Edit the file: [languages.html](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/stirling-pdf/src/main/resources/templates/fragments/languages.html)
For example, to add Polish, you would add: For example, to add Polish, you would add:
@ -25,7 +25,7 @@ The `data-bs-language-code` is the code used to reference the file in the next s
Start by copying the existing English property file: Start by copying the existing English property file:
- [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties) - [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/stirling-pdf/src/main/resources/messages_en_GB.properties)
Copy and rename it to `messages_{your data-bs-language-code here}.properties`. In the Polish example, you would set the name to `messages_pl_PL.properties`. Copy and rename it to `messages_{your data-bs-language-code here}.properties`. In the Polish example, you would set the name to `messages_pl_PL.properties`.

View File

@ -116,46 +116,46 @@ Stirling-PDF currently supports 40 languages!
| Language | Progress | | Language | Progress |
| -------------------------------------------- | -------------------------------------- | | -------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![72%](https://geps.dev/progress/72) | | Arabic (العربية) (ar_AR) | ![71%](https://geps.dev/progress/71) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![72%](https://geps.dev/progress/72) | | Azerbaijani (Azərbaycan Dili) (az_AZ) | ![72%](https://geps.dev/progress/72) |
| Basque (Euskara) (eu_ES) | ![42%](https://geps.dev/progress/42) | | Basque (Euskara) (eu_ES) | ![42%](https://geps.dev/progress/42) |
| Bulgarian (Български) (bg_BG) | ![80%](https://geps.dev/progress/80) | | Bulgarian (Български) (bg_BG) | ![79%](https://geps.dev/progress/79) |
| Catalan (Català) (ca_CA) | ![79%](https://geps.dev/progress/79) | | Catalan (Català) (ca_CA) | ![78%](https://geps.dev/progress/78) |
| Croatian (Hrvatski) (hr_HR) | ![71%](https://geps.dev/progress/71) | | Croatian (Hrvatski) (hr_HR) | ![70%](https://geps.dev/progress/70) |
| Czech (Česky) (cs_CZ) | ![82%](https://geps.dev/progress/82) | | Czech (Česky) (cs_CZ) | ![81%](https://geps.dev/progress/81) |
| Danish (Dansk) (da_DK) | ![72%](https://geps.dev/progress/72) | | Danish (Dansk) (da_DK) | ![71%](https://geps.dev/progress/71) |
| Dutch (Nederlands) (nl_NL) | ![70%](https://geps.dev/progress/70) | | Dutch (Nederlands) (nl_NL) | ![69%](https://geps.dev/progress/69) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![81%](https://geps.dev/progress/81) | | French (Français) (fr_FR) | ![80%](https://geps.dev/progress/80) |
| German (Deutsch) (de_DE) | ![98%](https://geps.dev/progress/98) | | German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
| Greek (Ελληνικά) (el_GR) | ![79%](https://geps.dev/progress/79) | | Greek (Ελληνικά) (el_GR) | ![78%](https://geps.dev/progress/78) |
| Hindi (हिंदी) (hi_IN) | ![78%](https://geps.dev/progress/78) | | Hindi (हिंदी) (hi_IN) | ![78%](https://geps.dev/progress/78) |
| Hungarian (Magyar) (hu_HU) | ![86%](https://geps.dev/progress/86) | | Hungarian (Magyar) (hu_HU) | ![89%](https://geps.dev/progress/89) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![72%](https://geps.dev/progress/72) | | Indonesian (Bahasa Indonesia) (id_ID) | ![72%](https://geps.dev/progress/72) |
| Irish (Gaeilge) (ga_IE) | ![80%](https://geps.dev/progress/80) | | Irish (Gaeilge) (ga_IE) | ![79%](https://geps.dev/progress/79) |
| Italian (Italiano) (it_IT) | ![96%](https://geps.dev/progress/96) | | Italian (Italiano) (it_IT) | ![97%](https://geps.dev/progress/97) |
| Japanese (日本語) (ja_JP) | ![80%](https://geps.dev/progress/80) | | Japanese (日本語) (ja_JP) | ![79%](https://geps.dev/progress/79) |
| Korean (한국어) (ko_KR) | ![79%](https://geps.dev/progress/79) | | Korean (한국어) (ko_KR) | ![78%](https://geps.dev/progress/78) |
| Norwegian (Norsk) (no_NB) | ![77%](https://geps.dev/progress/77) | | Norwegian (Norsk) (no_NB) | ![76%](https://geps.dev/progress/76) |
| Persian (فارسی) (fa_IR) | ![75%](https://geps.dev/progress/75) | | Persian (فارسی) (fa_IR) | ![74%](https://geps.dev/progress/74) |
| Polish (Polski) (pl_PL) | ![84%](https://geps.dev/progress/84) | | Polish (Polski) (pl_PL) | ![83%](https://geps.dev/progress/83) |
| Portuguese (Português) (pt_PT) | ![80%](https://geps.dev/progress/80) | | Portuguese (Português) (pt_PT) | ![79%](https://geps.dev/progress/79) |
| Portuguese Brazilian (Português) (pt_BR) | ![85%](https://geps.dev/progress/85) | | Portuguese Brazilian (Português) (pt_BR) | ![84%](https://geps.dev/progress/84) |
| Romanian (Română) (ro_RO) | ![67%](https://geps.dev/progress/67) | | Romanian (Română) (ro_RO) | ![66%](https://geps.dev/progress/66) |
| Russian (Русский) (ru_RU) | ![85%](https://geps.dev/progress/85) | | Russian (Русский) (ru_RU) | ![84%](https://geps.dev/progress/84) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![51%](https://geps.dev/progress/51) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![51%](https://geps.dev/progress/51) |
| Simplified Chinese (简体中文) (zh_CN) | ![85%](https://geps.dev/progress/85) | | Simplified Chinese (简体中文) (zh_CN) | ![84%](https://geps.dev/progress/84) |
| Slovakian (Slovensky) (sk_SK) | ![60%](https://geps.dev/progress/60) | | Slovakian (Slovensky) (sk_SK) | ![60%](https://geps.dev/progress/60) |
| Slovenian (Slovenščina) (sl_SI) | ![83%](https://geps.dev/progress/83) | | Slovenian (Slovenščina) (sl_SI) | ![82%](https://geps.dev/progress/82) |
| Spanish (Español) (es_ES) | ![87%](https://geps.dev/progress/87) | | Spanish (Español) (es_ES) | ![86%](https://geps.dev/progress/86) |
| Swedish (Svenska) (sv_SE) | ![76%](https://geps.dev/progress/76) | | Swedish (Svenska) (sv_SE) | ![76%](https://geps.dev/progress/76) |
| Thai (ไทย) (th_TH) | ![69%](https://geps.dev/progress/69) | | Thai (ไทย) (th_TH) | ![68%](https://geps.dev/progress/68) |
| Tibetan (བོད་ཡིག་) (bo_CN) | ![76%](https://geps.dev/progress/76) | | Tibetan (བོད་ཡིག་) (bo_CN) | ![75%](https://geps.dev/progress/75) |
| Traditional Chinese (繁體中文) (zh_TW) | ![85%](https://geps.dev/progress/85) | | Traditional Chinese (繁體中文) (zh_TW) | ![85%](https://geps.dev/progress/85) |
| Turkish (Türkçe) (tr_TR) | ![86%](https://geps.dev/progress/86) | | Turkish (Türkçe) (tr_TR) | ![85%](https://geps.dev/progress/85) |
| Ukrainian (Українська) (uk_UA) | ![85%](https://geps.dev/progress/85) | | Ukrainian (Українська) (uk_UA) | ![84%](https://geps.dev/progress/84) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![67%](https://geps.dev/progress/67) | | Vietnamese (Tiếng Việt) (vi_VN) | ![66%](https://geps.dev/progress/66) |
| Malayalam (മലയാളം) (ml_IN) | ![85%](https://geps.dev/progress/85) | | Malayalam (മലയാളം) (ml_IN) | ![85%](https://geps.dev/progress/85) |
## Stirling PDF Enterprise ## Stirling PDF Enterprise

View File

@ -26,21 +26,124 @@ ext {
bouncycastleVersion = "1.80" bouncycastleVersion = "1.80"
springSecuritySamlVersion = "6.5.0" springSecuritySamlVersion = "6.5.0"
openSamlVersion = "4.3.2" openSamlVersion = "4.3.2"
commonmarkVersion = "0.24.0"
tempJrePath = null tempJrePath = null
} }
group = "stirling.software" jar {
version = "0.46.2" enabled = false
manifest {
java { attributes "Implementation-Title": "Stirling-PDF",
// 17 is lowest but we support and recommend 21 "Implementation-Version": project.version
sourceCompatibility = JavaVersion.VERSION_17 }
} }
repositories { bootJar {
mavenCentral() enabled = false
maven { url = "https://build.shibboleth.net/maven/releases" } }
maven { url = "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
sourceSets {
main {
java {
if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false' || System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true'
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) {
exclude 'stirling/software/proprietary/security/**'
}
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
exclude 'stirling/software/SPDF/UI/impl/**'
}
}
}
test {
java {
if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false' || System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true'
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) {
exclude 'stirling/software/proprietary/security/**'
}
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
exclude 'stirling/software/SPDF/UI/impl/**'
}
}
}
}
allprojects {
group = 'stirling.software'
version = '0.46.2'
configurations.configureEach {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'com.diffplug.spotless'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
java {
// 17 is lowest but we support and recommend 21
sourceCompatibility = JavaVersion.VERSION_17
}
bootJar {
enabled = false
}
repositories {
mavenCentral()
}
configurations.configureEach {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
// Exclude vulnerable BouncyCastle version used in tableau
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'
exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on'
exclude group: 'org.bouncycastle', module: 'bcmail-jdk15on'
}
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.github.pixee:java-security-toolkit:1.2.1'
//tmp for security bumps
implementation 'ch.qos.logback:logback-core:1.5.18'
implementation 'ch.qos.logback:logback-classic:1.5.18'
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
}
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
dependsOn "spotlessApply"
}
compileJava {
options.compilerArgs << "-parameters"
}
test {
useJUnitPlatform()
}
} }
licenseReport { licenseReport {
@ -99,10 +202,9 @@ jpackage {
mainJar = "Stirling-PDF-${project.version}.jar" mainJar = "Stirling-PDF-${project.version}.jar"
appName = "Stirling PDF" appName = "Stirling PDF"
appVersion = project.version appVersion = project.version
// appVersion = "2005.45.1"
vendor = "Stirling PDF Inc" vendor = "Stirling PDF Inc"
appDescription = "Stirling PDF - Your Local PDF Editor" appDescription = "Stirling PDF - Your Local PDF Editor"
icon = "src/main/resources/static/favicon.ico" icon = "stirling-pdf/src/main/resources/static/favicon.ico"
verbose = true verbose = true
// mainClass = "org.springframework.boot.loader.launch.JarLauncher" // mainClass = "org.springframework.boot.loader.launch.JarLauncher"
@ -141,10 +243,10 @@ jpackage {
installDir = "C:/Program Files/Stirling-PDF" installDir = "C:/Program Files/Stirling-PDF"
} }
// macOS-specific configuration // MacOS-specific configuration
mac { mac {
appVersion = getMacVersion(project.version.toString()) appVersion = getMacVersion(project.version.toString())
icon = "src/main/resources/static/favicon.icns" icon = "stirling-pdf/src/main/resources/static/favicon.icns"
type = "dmg" type = "dmg"
macPackageIdentifier = "Stirling PDF" macPackageIdentifier = "Stirling PDF"
macPackageName = "Stirling PDF" macPackageName = "Stirling PDF"
@ -166,7 +268,7 @@ jpackage {
// Linux-specific configuration // Linux-specific configuration
linux { linux {
appVersion = project.version appVersion = project.version
icon = "src/main/resources/static/favicon.png" icon = "stirling-pdf/src/main/resources/static/favicon.png"
type = "deb" // Can also use "rpm" for Red Hat-based systems type = "deb" // Can also use "rpm" for Red Hat-based systems
// Debian package configuration // Debian package configuration
@ -202,10 +304,15 @@ jpackage {
]*/ ]*/
// Add copyright and license information // Add copyright and license information
copyright = "Copyright © 2024 Stirling Software" copyright = "Copyright © 2025 Stirling PDF Inc."
licenseFile = "LICENSE" licenseFile = "LICENSE"
} }
//tasks.wrapper {
// gradleVersion = "8.14"
// distributionType = Wrapper.DistributionType.ALL
//}
tasks.register('jpackageMacX64') { tasks.register('jpackageMacX64') {
group = 'distribution' group = 'distribution'
description = 'Packages app for MacOS x86_64' description = 'Packages app for MacOS x86_64'
@ -238,7 +345,7 @@ tasks.register('jpackageMacX64') {
'--main-class', 'org.springframework.boot.loader.launch.JarLauncher', '--main-class', 'org.springframework.boot.loader.launch.JarLauncher',
'--runtime-image', file(jrePath + "/zulu-17.jre/Contents/Home"), '--runtime-image', file(jrePath + "/zulu-17.jre/Contents/Home"),
'--dest', 'build/jpackage/x86_64', '--dest', 'build/jpackage/x86_64',
'--icon', 'src/main/resources/static/favicon.icns', '--icon', 'stirling-pdf/src/main/resources/static/favicon.icns',
'--app-version', getMacVersion(project.version.toString()), '--app-version', getMacVersion(project.version.toString()),
'--mac-package-name', 'Stirling PDF (x86_64)', '--mac-package-name', 'Stirling PDF (x86_64)',
'--mac-package-identifier', 'Stirling PDF (x86_64)', '--mac-package-identifier', 'Stirling PDF (x86_64)',
@ -287,12 +394,12 @@ tasks.register('downloadTempJre') {
def jreArchive = new File(tmpDir, 'jre.tar.gz') def jreArchive = new File(tmpDir, 'jre.tar.gz')
def jreDir = new File(tmpDir, 'jre') def jreDir = new File(tmpDir, 'jre')
println "Downloading JRE to $jreArchive..." println "Downloading JRE to $jreArchive"
jreArchive.withOutputStream { out -> jreArchive.withOutputStream { out ->
new URI(jreUrl).toURL().withInputStream { from -> out << from } new URI(jreUrl).toURL().withInputStream { from -> out << from }
} }
println "Extracting JRE to $jreDir..." println "Extracting JRE to $jreDir"
jreDir.mkdirs() jreDir.mkdirs()
providers.exec { providers.exec {
commandLine 'tar', '-xzf', jreArchive.absolutePath, '-C', jreDir.absolutePath, '--strip-components=1' commandLine 'tar', '-xzf', jreArchive.absolutePath, '-C', jreDir.absolutePath, '--strip-components=1'
@ -324,7 +431,7 @@ tasks.register('cleanTempJre') {
} }
launch4j { launch4j {
icon = "${projectDir}/src/main/resources/static/favicon.ico" icon = "${projectDir}/stirling-pdf/src/main/resources/static/favicon.ico"
outfile="Stirling-PDF.exe" outfile="Stirling-PDF.exe"
@ -335,7 +442,7 @@ launch4j {
} }
jarTask = tasks.bootJar jarTask = tasks.bootJar
errTitle="Encountered error, Do you have Java 21?" errTitle="Encountered error, do you have Java 21?"
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe" downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') { if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
@ -377,159 +484,12 @@ sonar {
property "sonar.projectKey", "Stirling-Tools_Stirling-PDF" property "sonar.projectKey", "Stirling-Tools_Stirling-PDF"
property "sonar.organization", "stirling-tools" property "sonar.organization", "stirling-tools"
property "sonar.exclusions", "**/build-wrapper-dump.json, src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" property "sonar.exclusions", "**/build-wrapper-dump.json, **/src/main/java/org/apache/**, **/src/main/resources/static/pdfjs/**, **/src/main/resources/static/pdfjs-legacy/**, **/src/main/resources/static/js/thirdParty/**"
property "sonar.coverage.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" property "sonar.coverage.exclusions", "**/src/main/java/org/apache/**, **/src/main/resources/static/pdfjs/**, **/src/main/resources/static/pdfjs-legacy/**, **/src/main/resources/static/js/thirdParty/**"
property "sonar.cpd.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" property "sonar.cpd.exclusions", "**/src/main/java/org/apache/**, **/src/main/resources/static/pdfjs/**, **/src/main/resources/static/pdfjs-legacy/**, **/src/main/resources/static/js/thirdParty/**"
} }
} }
//gradleLint {
// rules=['unused-dependency']
// }
tasks.wrapper {
gradleVersion = "8.14"
distributionType = Wrapper.DistributionType.ALL
}
//tasks.withType(JavaCompile) {
// options.compilerArgs << "-Xlint:deprecation"
//}
configurations.all {
// Remove all commons-logging dependencies so that only spring-jcl is used
exclude group: 'commons-logging', module: 'commons-logging'
// Exclude Tomcat
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
}
dependencies {
implementation project(':common')
//tmp for security bumps
implementation 'ch.qos.logback:logback-core:1.5.18'
implementation 'ch.qos.logback:logback-classic:1.5.18'
// Exclude vulnerable BouncyCastle version used in tableau
configurations.all {
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'
exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on'
exclude group: 'org.bouncycastle', module: 'bcmail-jdk15on'
}
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
implementation "me.friwi:jcefmaven:135.0.20"
implementation "org.openjfx:javafx-controls:21"
implementation "org.openjfx:javafx-swing:21"
}
//security updates
implementation "org.springframework:spring-webmvc:6.2.7"
implementation("io.github.pixee:java-security-toolkit:1.2.1")
// Exclude Tomcat and include Jetty
// implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion"
// implementation "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion"
implementation 'com.posthog.java:posthog:1.2.0'
implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1'
implementation 'org.snakeyaml:snakeyaml-engine:2.9'
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false" || System.getenv('DISABLE_ADDITIONAL_FEATURES') != 'true'
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') != 'true')) {
implementation project(':proprietary')
}
// Batik
implementation "org.apache.xmlgraphics:batik-all:1.19"
// TwelveMonkeys
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-psd:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion"
// Image metadata extractor
implementation "com.drewnoakes:metadata-extractor:2.19.0"
implementation "commons-io:commons-io:2.19.0"
// implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8"
// General PDF
// https://mvnrepository.com/artifact/com.opencsv/opencsv
implementation ("com.opencsv:opencsv:5.11.1")
// implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion")
implementation "org.apache.pdfbox:preflight:$pdfboxVersion"
implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion")
// https://mvnrepository.com/artifact/technology.tabula/tabula
implementation ('technology.tabula:tabula:1.0.5') {
exclude group: "org.slf4j", module: "slf4j-simple"
exclude group: "org.bouncycastle", module: "bcprov-jdk15on"
exclude group: "com.google.code.gson", module: "gson"
exclude group: "commons-io", module: "commons-io"
}
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4'
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
implementation "io.micrometer:micrometer-core:1.15.0"
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark:0.24.0"
implementation "org.commonmark:commonmark-ext-gfm-tables:0.24.0"
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
implementation "com.fathzer:javaluator:3.0.6"
implementation 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8'
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
// Mockito (core)
testImplementation 'org.mockito:mockito-core:5.18.0'
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
}
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
dependsOn "spotlessApply"
}
compileJava {
options.compilerArgs << "-parameters"
}
task writeVersion {
def propsFile = file("$projectDir/src/main/resources/version.properties")
def propsDir = propsFile.parentFile
doLast {
if (!propsDir.exists()) {
propsDir.mkdirs()
}
def props = new Properties()
props.setProperty("version", version)
props.store(propsFile.newWriter(), null)
}
}
processResources.dependsOn(writeVersion)
swaggerhubUpload { swaggerhubUpload {
// dependsOn = generateOpenApiDocs // Depends on your task generating Swagger docs // dependsOn = generateOpenApiDocs // Depends on your task generating Swagger docs
api = "Stirling-PDF" // The name of your API on SwaggerHub api = "Stirling-PDF" // The name of your API on SwaggerHub
@ -540,25 +500,43 @@ swaggerhubUpload {
oas = "3.0.0" // The version of the OpenAPI Specification you"re using oas = "3.0.0" // The version of the OpenAPI Specification you"re using
} }
jar {
enabled = false
manifest {
attributes "Implementation-Title": "Stirling-PDF",
"Implementation-Version": project.version
}
}
tasks.named("test") { tasks.named("test") {
useJUnitPlatform() useJUnitPlatform()
} }
task printVersion { tasks.register('writeVersion') {
def propsFile = file("$projectDir/stirling-pdf/src/main/resources/version.properties")
def propsDir = propsFile.parentFile
doLast {
if (propsDir.exists()) {
if (propsFile.exists()) {
println "File exists: $propsFile"
} else {
println "$propsFile does not exist. Creating file."
propsFile.createNewFile()
}
} else {
println "Creating directory: $propsDir"
propsDir.mkdirs()
propsFile.createNewFile()
}
def props = new Properties()
props.setProperty("version", version)
props.store(propsFile.newWriter(), null)
}
}
processResources.dependsOn(writeVersion)
tasks.register('printVersion') {
doLast { doLast {
println project.version println project.version
} }
} }
task printMacVersion { tasks.register('printMacVersion') {
doLast { doLast {
println getMacVersion(project.version.toString()) println getMacVersion(project.version.toString())
} }

2
common/.gitignore vendored
View File

@ -124,6 +124,7 @@ SwaggerDoc.json
*.rar *.rar
*.db *.db
/build /build
/common/build/
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
@ -193,4 +194,3 @@ id_ed25519.pub
# node_modules # node_modules
node_modules/ node_modules/
*.mjs

View File

@ -1,41 +1,9 @@
plugins {
id 'java-library'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'stirling.software'
version = '0.46.2'
ext {
lombokVersion = "1.18.38"
}
java {
sourceCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
configurations.all {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
}
dependencyManagement {
imports {
mavenBom 'org.springframework.boot:spring-boot-dependencies:3.5.0'
}
}
dependencies { dependencies {
api 'org.springframework.boot:spring-boot-starter-web' api 'org.springframework.boot:spring-boot-starter-web'
api 'org.springframework.boot:spring-boot-starter-thymeleaf' api 'org.springframework.boot:spring-boot-starter-thymeleaf'
api 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1' api 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1'
api 'com.fathzer:javaluator:3.0.6' api 'com.fathzer:javaluator:3.0.6'
api 'com.posthog.java:posthog:1.2.0' api 'com.posthog.java:posthog:1.2.0'
api 'io.github.pixee:java-security-toolkit:1.2.1'
api 'org.apache.commons:commons-lang3:3.17.0' api 'org.apache.commons:commons-lang3:3.17.0'
api 'com.drewnoakes:metadata-extractor:2.19.0' // Image metadata extractor api 'com.drewnoakes:metadata-extractor:2.19.0' // Image metadata extractor
api 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8' api 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8'
@ -43,10 +11,5 @@ dependencies {
api 'jakarta.servlet:jakarta.servlet-api:6.1.0' api 'jakarta.servlet:jakarta.servlet-api:6.1.0'
api 'org.snakeyaml:snakeyaml-engine:2.9' api 'org.snakeyaml:snakeyaml-engine:2.9'
api "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8" api "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8"
api 'jakarta.mail:jakarta.mail-api:2.1.3'
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
testImplementation "org.springframework.boot:spring-boot-starter-test"
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
} }

View File

@ -1,5 +1,7 @@
package stirling.software.common.configuration; package stirling.software.common.configuration;
import io.github.pixee.security.SystemCommand;
import jakarta.annotation.PostConstruct;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -149,12 +151,12 @@ public class AppConfig {
@Bean(name = "activeSecurity") @Bean(name = "activeSecurity")
public boolean activeSecurity() { public boolean activeSecurity() {
String additionalFeaturesOff = env.getProperty("DISABLE_ADDITIONAL_FEATURES"); String disableAdditionalFeatures = env.getProperty("DISABLE_ADDITIONAL_FEATURES");
if (additionalFeaturesOff != null) { if (disableAdditionalFeatures != null) {
// DISABLE_ADDITIONAL_FEATURES=true means security OFF, so return false // DISABLE_ADDITIONAL_FEATURES=true means security OFF, so return false
// DISABLE_ADDITIONAL_FEATURES=false means security ON, so return true // DISABLE_ADDITIONAL_FEATURES=false means security ON, so return true
return !Boolean.parseBoolean(additionalFeaturesOff); return !Boolean.parseBoolean(disableAdditionalFeatures);
} }
return env.getProperty("DOCKER_ENABLE_SECURITY", Boolean.class, true); return env.getProperty("DOCKER_ENABLE_SECURITY", Boolean.class, true);
@ -164,7 +166,7 @@ public class AppConfig {
@ConditionalOnMissingClass( @ConditionalOnMissingClass(
"stirling.software.proprietary.security.configuration.SecurityConfiguration") "stirling.software.proprietary.security.configuration.SecurityConfiguration")
public boolean missingActiveSecurity() { public boolean missingActiveSecurity() {
return false; return true;
} }
@Bean(name = "directoryFilter") @Bean(name = "directoryFilter")

View File

@ -344,10 +344,10 @@ public class ApplicationProperties {
@Override @Override
public String toString() { public String toString() {
return """ return """
Driver { Driver {
driverName='%s' driverName='%s'
} }
""" """
.formatted(driverName); .formatted(driverName);
} }
} }

View File

@ -0,0 +1,39 @@
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
@EqualsAndHashCode(callSuper = true)
public class EmlToPdfRequest extends PDFFile {
// fileInput is inherited from PDFFile
@Schema(
description = "Include email attachments in the PDF output",
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
example = "false")
private boolean includeAttachments = false;
@Schema(
description = "Maximum attachment size in MB to include (default 10MB, range: 1-100)",
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
example = "10",
minimum = "1",
maximum = "100")
private int maxAttachmentSizeMB = 10;
@Schema(
description = "Download HTML intermediate file instead of PDF",
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
example = "false")
private boolean downloadHtml = false;
@Schema(
description = "Include CC and BCC recipients in header (if available)",
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
example = "true")
private boolean includeAllRecipients = true;
}

View File

@ -208,7 +208,7 @@ public class PostHogService {
// New environment variables // New environment variables
dockerMetrics.put("version_tag", System.getenv("VERSION_TAG")); dockerMetrics.put("version_tag", System.getenv("VERSION_TAG"));
dockerMetrics.put("without_enhanced_features", System.getenv("WITHOUT_ENHANCED_FEATURES")); dockerMetrics.put("additional_features_off", System.getenv("ADDITIONAL_FEATURES_OFF"));
dockerMetrics.put("fat_docker", System.getenv("FAT_DOCKER")); dockerMetrics.put("fat_docker", System.getenv("FAT_DOCKER"));
return dockerMetrics; return dockerMetrics;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
package stirling.software.common.util;
import java.util.Collection;
public class ValidationUtil {
public static boolean isStringEmpty(String input) {
return input == null || input.isBlank();
}
public static boolean isCollectionEmpty(Collection<String> input) {
return input == null || input.isEmpty();
}
}

View File

@ -1,5 +1,14 @@
package stirling.software.common.util; package stirling.software.common.util;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -10,18 +19,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
class CheckProgramInstallTest { class CheckProgramInstallTest {
private MockedStatic<ProcessExecutor> mockProcessExecutor; private MockedStatic<ProcessExecutor> mockProcessExecutor;

View File

@ -19,7 +19,6 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import stirling.software.common.configuration.RuntimePathConfig; import stirling.software.common.configuration.RuntimePathConfig;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)

View File

@ -1,10 +1,7 @@
package stirling.software.common.util; package stirling.software.common.util;
import static org.mockito.Mockito.*;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -12,11 +9,13 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import stirling.software.common.model.enumeration.UsernameAttribute; import stirling.software.common.model.enumeration.UsernameAttribute;
import stirling.software.common.model.oauth2.GitHubProvider; import stirling.software.common.model.oauth2.GitHubProvider;
import stirling.software.common.model.oauth2.GoogleProvider; import stirling.software.common.model.oauth2.GoogleProvider;
import stirling.software.common.model.oauth2.Provider; import stirling.software.common.model.oauth2.Provider;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class ProviderUtilsTest { class ProviderUtilsTest {
@ -29,19 +28,19 @@ class ProviderUtilsTest {
when(provider.getClientSecret()).thenReturn("clientSecret"); when(provider.getClientSecret()).thenReturn("clientSecret");
when(provider.getScopes()).thenReturn(List.of("read:user")); when(provider.getScopes()).thenReturn(List.of("read:user"));
Assertions.assertTrue(ProviderUtils.validateProvider(provider)); assertTrue(ProviderUtils.validateProvider(provider));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("providerParams") @MethodSource("providerParams")
void testUnsuccessfulValidation(Provider provider) { void testUnsuccessfulValidation(Provider provider) {
Assertions.assertFalse(ProviderUtils.validateProvider(provider)); assertFalse(ProviderUtils.validateProvider(provider));
} }
public static Stream<Arguments> providerParams() { public static Stream<Arguments> providerParams() {
Provider generic = null; Provider generic = null;
var google = var google =
new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL); new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL);
var github = new GitHubProvider("clientId", "", List.of("scope"), UsernameAttribute.LOGIN); var github = new GitHubProvider("clientId", "", List.of("scope"), UsernameAttribute.LOGIN);
return Stream.of(Arguments.of(generic), Arguments.of(google), Arguments.of(github)); return Stream.of(Arguments.of(generic), Arguments.of(google), Arguments.of(github));

View File

@ -1,12 +1,10 @@
package stirling.software.common.util.misc; package stirling.software.common.util.misc;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import stirling.software.common.model.api.misc.HighContrastColorCombination; import stirling.software.common.model.api.misc.HighContrastColorCombination;
import stirling.software.common.model.api.misc.ReplaceAndInvert; import stirling.software.common.model.api.misc.ReplaceAndInvert;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
class HighContrastColorReplaceDeciderTest { class HighContrastColorReplaceDeciderTest {

View File

@ -26,7 +26,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import stirling.software.common.model.api.misc.ReplaceAndInvert; import stirling.software.common.model.api.misc.ReplaceAndInvert;
class InvertFullColorStrategyTest { class InvertFullColorStrategyTest {

View File

@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import stirling.software.common.model.api.misc.ReplaceAndInvert; import stirling.software.common.model.api.misc.ReplaceAndInvert;
class ReplaceAndInvertColorStrategyTest { class ReplaceAndInvertColorStrategyTest {

View File

@ -1,17 +1,14 @@
package stirling.software.common.util.propertyeditor; package stirling.software.common.util.propertyeditor;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import stirling.software.common.model.api.security.RedactionArea;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import stirling.software.common.model.api.security.RedactionArea;
class StringToArrayListPropertyEditorTest { class StringToArrayListPropertyEditorTest {
private StringToArrayListPropertyEditor editor; private StringToArrayListPropertyEditor editor;

View File

@ -14,9 +14,9 @@ services:
ports: ports:
- "8080:8080" - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - ./stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - ./stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw - ./stirling/latest/logs:/logs:rw
environment: environment:
DISABLE_ADDITIONAL_FEATURES: "false" DISABLE_ADDITIONAL_FEATURES: "false"
SECURITY_ENABLELOGIN: "true" SECURITY_ENABLELOGIN: "true"

View File

@ -14,9 +14,9 @@ services:
ports: ports:
- "8080:8080" - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - ./stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - ./stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw - ./stirling/latest/logs:/logs:rw
environment: environment:
DISABLE_ADDITIONAL_FEATURES: "false" DISABLE_ADDITIONAL_FEATURES: "false"
SECURITY_ENABLELOGIN: "true" SECURITY_ENABLELOGIN: "true"

View File

@ -14,8 +14,8 @@ services:
ports: ports:
- "8080:8080" - "8080:8080"
volumes: volumes:
- /stirling/latest/config:/configs:rw - ./stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw - ./stirling/latest/logs:/logs:rw
environment: environment:
DISABLE_ADDITIONAL_FEATURES: "true" DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false" SECURITY_ENABLELOGIN: "false"

View File

@ -14,9 +14,9 @@ services:
ports: ports:
- "8080:8080" - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - ./stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - ./stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw - ./stirling/latest/logs:/logs:rw
environment: environment:
DISABLE_ADDITIONAL_FEATURES: "true" DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false" SECURITY_ENABLELOGIN: "false"

View File

@ -14,9 +14,9 @@ services:
ports: ports:
- 8080:8080 - 8080:8080
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - ./stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - ./stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw - ./stirling/latest/logs:/logs:rw
environment: environment:
DISABLE_ADDITIONAL_FEATURES: "false" DISABLE_ADDITIONAL_FEATURES: "false"
SECURITY_ENABLELOGIN: "true" SECURITY_ENABLELOGIN: "true"

View File

@ -124,6 +124,7 @@ SwaggerDoc.json
*.rar *.rar
*.db *.db
/build /build
/proprietary/build/
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
@ -193,4 +194,3 @@ id_ed25519.pub
# node_modules # node_modules
node_modules/ node_modules/
*.mjs

View File

@ -1,28 +1,7 @@
plugins {
id 'java-library'
id 'io.spring.dependency-management' version '1.1.7'
}
repositories { repositories {
mavenCentral()
maven { url = "https://build.shibboleth.net/maven/releases" } maven { url = "https://build.shibboleth.net/maven/releases" }
} }
java {
sourceCompatibility = JavaVersion.VERSION_17
}
configurations.all {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
}
dependencyManagement {
imports {
mavenBom 'org.springframework.boot:spring-boot-dependencies:3.5.0'
}
}
dependencies { dependencies {
implementation project(':common') implementation project(':common')
@ -42,7 +21,6 @@ dependencies {
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17 // https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation 'org.bouncycastle:bcprov-jdk18on:1.80' implementation 'org.bouncycastle:bcprov-jdk18on:1.80'
implementation 'io.github.pixee:java-security-toolkit:1.2.1'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
api 'io.micrometer:micrometer-registry-prometheus' api 'io.micrometer:micrometer-registry-prometheus'
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5' implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
@ -54,14 +32,6 @@ dependencies {
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion" implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
} }
implementation 'com.coveo:saml-client:5.0.0' implementation 'com.coveo:saml-client:5.0.0'
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
} }
tasks.register('prepareKotlinBuildScriptModel') {} tasks.register('prepareKotlinBuildScriptModel') {}

View File

@ -38,6 +38,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
public static final String LOGOUT_PATH = "/login?logout=true"; public static final String LOGOUT_PATH = "/login?logout=true";
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private final AppConfig appConfig; private final AppConfig appConfig;
@Override @Override

View File

@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@ -55,6 +56,7 @@ import stirling.software.proprietary.security.session.SessionPersistentRegistry;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableMethodSecurity @EnableMethodSecurity
@DependsOn("runningProOrHigher")
public class SecurityConfiguration { public class SecurityConfiguration {
private final CustomUserDetailsService userDetailsService; private final CustomUserDetailsService userDetailsService;

View File

@ -1,13 +1,13 @@
package stirling.software.SPDF.EE; package stirling.software.proprietary.security.configuration.ee;
import static stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.ApplicationProperties.EnterpriseEdition; import stirling.software.common.model.ApplicationProperties.EnterpriseEdition;
import stirling.software.common.model.ApplicationProperties.Premium; import stirling.software.common.model.ApplicationProperties.Premium;
@ -15,7 +15,6 @@ import stirling.software.common.model.ApplicationProperties.Premium.ProFeatures.
@Configuration @Configuration
@Order(Ordered.HIGHEST_PRECEDENCE) @Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class EEAppConfig { public class EEAppConfig {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
@ -30,6 +29,7 @@ public class EEAppConfig {
} }
@Bean(name = "runningProOrHigher") @Bean(name = "runningProOrHigher")
@Qualifier("runningProOrHigher")
public boolean runningProOrHigher() { public boolean runningProOrHigher() {
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL; return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
} }

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.EE; package stirling.software.proprietary.security.configuration.ee;
import java.net.URI; import java.net.URI;
import java.net.http.HttpClient; import java.net.http.HttpClient;
@ -27,7 +27,7 @@ import stirling.software.common.util.GeneralUtils;
@RequiredArgsConstructor @RequiredArgsConstructor
public class KeygenLicenseVerifier { public class KeygenLicenseVerifier {
enum License { public enum License {
NORMAL, NORMAL,
PRO, PRO,
ENTERPRISE ENTERPRISE

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.EE; package stirling.software.proprietary.security.configuration.ee;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -10,12 +10,12 @@ import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.util.GeneralUtils; import stirling.software.common.util.GeneralUtils;
import stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
@Component
@Slf4j @Slf4j
@Component
public class LicenseKeyChecker { public class LicenseKeyChecker {
private static final String FILE_PREFIX = "file:"; private static final String FILE_PREFIX = "file:";

View File

@ -5,7 +5,6 @@ import java.util.Collections;
import java.util.UUID; import java.util.UUID;
import org.opensaml.saml.saml2.core.AuthnRequest; import org.opensaml.saml.saml2.core.AuthnRequest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -30,8 +29,8 @@ import stirling.software.common.model.ApplicationProperties.Security.SAML2;
@Configuration @Configuration
@Slf4j @Slf4j
@ConditionalOnProperty(value = "security.saml2.enabled", havingValue = "true")
@RequiredArgsConstructor @RequiredArgsConstructor
@ConditionalOnBooleanProperty("security.saml2.enabled")
public class SAML2Configuration { public class SAML2Configuration {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;

View File

@ -1,7 +1,10 @@
package stirling.software.SPDF.EE; package stirling.software.proprietary.security.configuration.ee;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -13,7 +16,6 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)

View File

@ -1,5 +1,5 @@
echo "Running Stirling PDF with DISABLE_ADDITIONAL_FEATURES=${DISABLE_ADDITIONAL_FEATURES} and VERSION_TAG=${VERSION_TAG}" echo "Running Stirling PDF with DISABLE_ADDITIONAL_FEATURES=${DISABLE_ADDITIONAL_FEATURES} and VERSION_TAG=${VERSION_TAG}"
# Check for $DISABLE_ADDITIONAL_FEATURES and download the appropriate JAR if required # Check for DISABLE_ADDITIONAL_FEATURES and download the appropriate JAR if required
if [ "$DISABLE_ADDITIONAL_FEATURES" = "false" ] && [ "$VERSION_TAG" != "alpha" ]; then if [ "$DISABLE_ADDITIONAL_FEATURES" = "false" ] && [ "$VERSION_TAG" != "alpha" ]; then
if [ ! -f app-security.jar ]; then if [ ! -f app-security.jar ]; then
echo "Trying to download from: https://files.stirlingpdf.com/v$VERSION_TAG/Stirling-PDF-with-login.jar" echo "Trying to download from: https://files.stirlingpdf.com/v$VERSION_TAG/Stirling-PDF-with-login.jar"

View File

@ -4,4 +4,4 @@ plugins {
} }
rootProject.name = 'Stirling-PDF' rootProject.name = 'Stirling-PDF'
include 'common', 'proprietary' include 'stirling-pdf', 'common', 'proprietary'

View File

@ -1,38 +0,0 @@
package stirling.software.SPDF.model.api;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ConditionalOnProperty(value = "mail.enabled", havingValue = "true", matchIfMissing = false)
public class Email extends GeneralFile {
@Schema(
description = "The recipient's email address",
requiredMode = Schema.RequiredMode.REQUIRED,
format = "email")
private String to;
@Schema(
description = "The subject of the email",
defaultValue = "Stirling Software PDF Notification",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String subject;
@Schema(
description = "The body of the email",
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
defaultValue =
"This message was automatically generated by Stirling-PDF, an innovative"
+ " solution from Stirling Software. For more information, visit our <a"
+ " href=\"https://stirling-software.com\">website</a>.<br><br>Please do"
+ " not reply directly to this email.")
private String body;
}

View File

@ -1,19 +0,0 @@
package stirling.software.SPDF.model.api;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode
public class GeneralFile {
@Schema(
description = "The input file",
requiredMode = Schema.RequiredMode.REQUIRED,
format = "binary")
private MultipartFile fileInput;
}

View File

@ -1,42 +0,0 @@
package stirling.software.SPDF.model.api.converters;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@Data
@EqualsAndHashCode(callSuper = true)
public class ConvertToImageRequest extends PDFWithPageNums {
@Schema(
description = "The output image format",
defaultValue = "png",
allowableValues = {"png", "jpeg", "jpg", "gif", "webp"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String imageFormat;
@Schema(
description =
"Choose between a single image containing all pages or separate images for each"
+ " page",
defaultValue = "multiple",
allowableValues = {"single", "multiple"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String singleOrMultiple;
@Schema(
description = "The color type of the output image(s)",
defaultValue = "color",
allowableValues = {"color", "greyscale", "blackwhite"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String colorType;
@Schema(
description = "The DPI (dots per inch) for the output image(s)",
defaultValue = "300",
requiredMode = Schema.RequiredMode.REQUIRED)
private Integer dpi;
}

View File

@ -1,46 +0,0 @@
package stirling.software.SPDF.model.api.general;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class OverlayPdfsRequest extends PDFFile {
@Schema(
description =
"An array of PDF files to be used as overlays on the base PDF. The order in"
+ " these files is applied based on the selected mode.",
requiredMode = Schema.RequiredMode.REQUIRED)
private MultipartFile[] overlayFiles;
@Schema(
description =
"The mode of overlaying: 'SequentialOverlay' for sequential application,"
+ " 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay'"
+ " for fixed repetition based on provided counts",
allowableValues = {"SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String overlayMode;
@Schema(
description =
"An array of integers specifying the number of times each corresponding overlay"
+ " file should be applied in the 'FixedRepeatOverlay' mode. This should"
+ " match the length of the overlayFiles array.",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private int[] counts;
@Schema(
description = "Overlay position 0 is Foregound, 1 is Background",
allowableValues = {"0", "1"},
requiredMode = Schema.RequiredMode.REQUIRED,
type = "number")
private int overlayPosition;
}

View File

@ -1,87 +0,0 @@
package stirling.software.SPDF.model.api.misc;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@Data
@EqualsAndHashCode(callSuper = true)
public class AddStampRequest extends PDFWithPageNums {
@Schema(
description = "The stamp type (text or image)",
allowableValues = {"text", "image"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String stampType;
@Schema(description = "The stamp text", defaultValue = "Stirling Software")
private String stampText;
@Schema(description = "The stamp image")
private MultipartFile stampImage;
@Schema(
description = "The selected alphabet of the stamp text",
allowableValues = {"roman", "arabic", "japanese", "korean", "chinese"},
defaultValue = "roman")
private String alphabet = "roman";
@Schema(
description = "The font size of the stamp text and image",
defaultValue = "30",
requiredMode = Schema.RequiredMode.REQUIRED)
private float fontSize;
@Schema(
description = "The rotation of the stamp in degrees",
defaultValue = "0",
requiredMode = Schema.RequiredMode.REQUIRED)
private float rotation;
@Schema(
description = "The opacity of the stamp (0.0 - 1.0)",
defaultValue = "0.5",
requiredMode = Schema.RequiredMode.REQUIRED)
private float opacity;
@Schema(
description =
"Position for stamp placement based on a 1-9 grid (1: bottom-left, 2: bottom-center,"
+ " 3: bottom-right, 4: middle-left, 5: middle-center, 6: middle-right,"
+ " 7: top-left, 8: top-center, 9: top-right)",
allowableValues = {"1", "2", "3", "4", "5", "6", "7", "8", "9"},
defaultValue = "5",
requiredMode = Schema.RequiredMode.REQUIRED)
private int position;
@Schema(
description =
"Override X coordinate for stamp placement. If set, it will override the"
+ " position-based calculation. Negative value means no override.",
defaultValue = "-1",
requiredMode = Schema.RequiredMode.REQUIRED)
private float overrideX; // Default to -1 indicating no override
@Schema(
description =
"Override Y coordinate for stamp placement. If set, it will override the"
+ " position-based calculation. Negative value means no override.",
defaultValue = "-1",
requiredMode = Schema.RequiredMode.REQUIRED)
private float overrideY; // Default to -1 indicating no override
@Schema(
description = "Specifies the margin size for the stamp.",
allowableValues = {"small", "medium", "large", "x-large"},
defaultValue = "medium",
requiredMode = Schema.RequiredMode.REQUIRED)
private String customMargin;
@Schema(description = "The color of the stamp text", defaultValue = "#d3d3d3")
private String customColor;
}

View File

@ -1,84 +0,0 @@
package stirling.software.SPDF.model.api.misc;
import java.util.Map;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class MetadataRequest extends PDFFile {
@Schema(
description = "Delete all metadata if set to true",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean deleteAll;
@Schema(
description = "The author of the document",
defaultValue = "author",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String author;
@Schema(
description = "The creation date of the document (format: yyyy/MM/dd HH:mm:ss)",
pattern = "yyyy/MM/dd HH:mm:ss",
defaultValue = "2023/10/01 12:00:00",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String creationDate;
@Schema(
description = "The creator of the document",
defaultValue = "creator",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String creator;
@Schema(
description = "The keywords for the document",
defaultValue = "keywords",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String keywords;
@Schema(
description = "The modification date of the document (format: yyyy/MM/dd HH:mm:ss)",
pattern = "yyyy/MM/dd HH:mm:ss",
defaultValue = "2023/10/01 12:00:00",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String modificationDate;
@Schema(
description = "The producer of the document",
defaultValue = "producer",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String producer;
@Schema(
description = "The subject of the document",
defaultValue = "subject",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String subject;
@Schema(
description = "The title of the document",
defaultValue = "title",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String title;
@Schema(
description = "The trapped status of the document",
defaultValue = "False",
allowableValues = {"True", "False", "Unknown"},
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String trapped;
@Schema(
description =
"Map list of key and value of custom parameters. Note these must start with"
+ " customKey and customValue if they are non-standard")
private Map<String, String> allRequestParams;
}

View File

@ -1,49 +0,0 @@
package stirling.software.SPDF.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class RedactPdfRequest extends PDFFile {
@Schema(
description = "List of text to redact from the PDF",
defaultValue = "text,text2",
requiredMode = Schema.RequiredMode.REQUIRED)
private String listOfText;
@Schema(
description = "Whether to use regex for the listOfText",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean useRegex;
@Schema(
description = "Whether to use whole word search",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean wholeWordSearch;
@Schema(
description = "The color for redaction",
defaultValue = "#000000",
requiredMode = Schema.RequiredMode.REQUIRED)
private String redactColor;
@Schema(
description = "Custom padding for redaction",
type = "number",
requiredMode = Schema.RequiredMode.REQUIRED)
private float customPadding;
@Schema(
description = "Convert the redacted PDF to an image",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean convertPDFToImage;
}

View File

@ -1,28 +0,0 @@
package stirling.software.SPDF.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode
public class RedactionArea {
@Schema(description = "The left edge point of the area to be redacted.")
private Double x;
@Schema(description = "The top edge point of the area to be redacted.")
private Double y;
@Schema(description = "The height of the area to be redacted.")
private Double height;
@Schema(description = "The width of the area to be redacted.")
private Double width;
@Schema(description = "The page on which the area should be redacted.")
private Integer page;
@Schema(description = "The color used to redact the specified area.")
private String color;
}

View File

@ -1,49 +0,0 @@
package stirling.software.SPDF.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class SanitizePdfRequest extends PDFFile {
@Schema(
description = "Remove JavaScript actions from the PDF",
defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeJavaScript;
@Schema(
description = "Remove embedded files from the PDF",
defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeEmbeddedFiles;
@Schema(
description = "Remove XMP metadata from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeXMPMetadata;
@Schema(
description = "Remove document info metadata from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeMetadata;
@Schema(
description = "Remove links from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeLinks;
@Schema(
description = "Remove fonts from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeFonts;
}

View File

@ -1,17 +0,0 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class UpdateUserDetails extends UpdateUserUsername {
@Schema(
description = "new password for user",
format = "password",
requiredMode = Schema.RequiredMode.REQUIRED)
private String newPassword;
}

View File

@ -1,14 +0,0 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class UpdateUserUsername extends UsernameAndPass {
@Schema(description = "new username for user")
private String newUsername;
}

View File

@ -1,14 +0,0 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode
public class Username {
@Schema(description = "username of user", requiredMode = Schema.RequiredMode.REQUIRED)
private String username;
}

View File

@ -1,14 +0,0 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class UsernameAndPass extends Username {
@Schema(description = "password of user", format = "password")
private String password;
}

196
stirling-pdf/.gitignore vendored Normal file
View File

@ -0,0 +1,196 @@
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.exe
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
.classpath
.project
version.properties
#### Stirling-PDF Files ###
pipeline/watchedFolders/
pipeline/finishedFolders/
customFiles/
configs/
watchedFolders/
clientWebUI/
!cucumber/
!cucumber/exampleFiles/
!cucumber/exampleFiles/example_html.zip
exampleYmlFiles/stirling/
/testing/file_snapshots
SwaggerDoc.json
# Gradle
.gradle
.lock
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project
### Eclipse Patch ###
# Spring Boot Tooling
.sts4-cache/
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
*.db
/build/*
/stirling-pdf/build/*
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.pyo
# Virtual environments
.env*
.venv*
env*/
venv*/
ENV/
env.bak/
venv.bak/
# VS Code
/.vscode/**/*
!/.vscode/settings.json
!/.vscode/extensions.json
# IntelliJ IDEA
.idea/
*.iml
out/
# Ignore Mac DS_Store files
.DS_Store
**/.DS_Store
# cucumber
/cucumber/reports/**
# Certs and Security Files
*.p12
*.pk8
*.pem
*.crt
*.cer
*.cert
*.der
*.key
*.csr
*.kdbx
*.jks
*.asc
# SSH Keys
*.pub
*.priv
id_rsa
id_rsa.pub
id_ecdsa
id_ecdsa.pub
id_ed25519
id_ed25519.pub
.ssh/
*ssh
# cache
.cache
.ruff_cache
.mypy_cache
.pytest_cache
.ipynb_checkpoints
**/jcef-bundle/
# node_modules
node_modules/

95
stirling-pdf/build.gradle Normal file
View File

@ -0,0 +1,95 @@
repositories {
maven { url = 'https://build.shibboleth.net/maven/releases' }
maven { url = 'https://maven.pkg.github.com/jcefmaven/jcefmaven' }
}
dependencies {
if (System.getenv('STIRLING_PDF_DESKTOP_UI') != 'false'
|| (project.hasProperty('STIRLING_PDF_DESKTOP_UI')
&& project.getProperty('STIRLING_PDF_DESKTOP_UI') != 'false')) {
implementation 'me.friwi:jcefmaven:132.3.1'
implementation 'org.openjfx:javafx-controls:21'
implementation 'org.openjfx:javafx-swing:21'
}
if (System.getenv('DISABLE_ADDITIONAL_FEATURES') != 'true'
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') != 'true')) {
implementation project(':proprietary')
}
implementation project(':common')
implementation 'org.springframework.boot:spring-boot-starter-jetty'
implementation 'com.posthog.java:posthog:1.2.0'
implementation 'commons-io:commons-io:2.19.0'
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation 'io.micrometer:micrometer-core:1.14.6'
implementation 'com.google.zxing:core:3.5.3'
implementation "org.commonmark:commonmark:$commonmarkVersion" // https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark-ext-gfm-tables:$commonmarkVersion"
// General PDF dependencies
implementation "org.apache.pdfbox:preflight:$pdfboxVersion"
implementation "org.apache.pdfbox:xmpbox:$pdfboxVersion"
// https://mvnrepository.com/artifact/technology.tabula/tabula
implementation ('technology.tabula:tabula:1.0.5') {
exclude group: 'org.slf4j', module: 'slf4j-simple'
exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'
exclude group: 'com.google.code.gson', module: 'gson'
}
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4'
implementation 'com.opencsv:opencsv:5.11.1' // https://mvnrepository.com/artifact/com.opencsv/opencsv
// Batik
implementation 'org.apache.xmlgraphics:batik-all:1.18'
// TwelveMonkeys
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-psd:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion"
// runtimeOnly "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion"
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}
sourceSets {
main {
resources {
srcDirs += ['../configs']
}
}
}
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
zip64 = true
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
manifest {
attributes(
'Main-Class': 'stirling.software.SPDF.SPDFApplication',
'Implementation-Title': 'Stirling-PDF',
'Implementation-Version': project.version
)
}
}
jar.dependsOn ':common:jar'
jar.dependsOn ':proprietary:jar'

View File

@ -11,7 +11,6 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@ -27,6 +26,7 @@ import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.UI.WebBrowser; import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.common.configuration.AppConfig;
import stirling.software.common.configuration.ConfigInitializer; import stirling.software.common.configuration.ConfigInitializer;
import stirling.software.common.configuration.InstallationPathConfig; import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
@ -36,9 +36,9 @@ import stirling.software.common.util.UrlUtils;
@EnableScheduling @EnableScheduling
@SpringBootApplication( @SpringBootApplication(
scanBasePackages = { scanBasePackages = {
"stirling.software.common",
"stirling.software.SPDF", "stirling.software.SPDF",
"stirling.software.proprietary.security" "stirling.software.common",
"stirling.software.proprietary"
}, },
exclude = { exclude = {
DataSourceAutoConfiguration.class, DataSourceAutoConfiguration.class,
@ -50,20 +50,17 @@ public class SPDFApplication {
private static String baseUrlStatic; private static String baseUrlStatic;
private static String contextPathStatic; private static String contextPathStatic;
private final AppConfig appConfig;
private final Environment env; private final Environment env;
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private final WebBrowser webBrowser; private final WebBrowser webBrowser;
@Value("${baseUrl:http://localhost}")
private String baseUrl;
@Value("${server.servlet.context-path:/}")
private String contextPath;
public SPDFApplication( public SPDFApplication(
AppConfig appConfig,
Environment env, Environment env,
ApplicationProperties applicationProperties, ApplicationProperties applicationProperties,
@Autowired(required = false) WebBrowser webBrowser) { @Autowired(required = false) WebBrowser webBrowser) {
this.appConfig = appConfig;
this.env = env; this.env = env;
this.applicationProperties = applicationProperties; this.applicationProperties = applicationProperties;
this.webBrowser = webBrowser; this.webBrowser = webBrowser;
@ -152,9 +149,14 @@ public class SPDFApplication {
@PostConstruct @PostConstruct
public void init() { public void init() {
baseUrlStatic = this.baseUrl; String baseUrl = appConfig.getBaseUrl();
contextPathStatic = this.contextPath; String contextPath = appConfig.getContextPath();
String serverPort = appConfig.getServerPort();
baseUrlStatic = baseUrl;
contextPathStatic = contextPath;
serverPortStatic = serverPort;
String url = baseUrl + ":" + getStaticPort() + contextPath; String url = baseUrl + ":" + getStaticPort() + contextPath;
if (webBrowser != null if (webBrowser != null
&& Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) { && Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
webBrowser.initWebUI(url); webBrowser.initWebUI(url);
@ -165,6 +167,7 @@ public class SPDFApplication {
try { try {
String os = System.getProperty("os.name").toLowerCase(); String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime(); Runtime rt = Runtime.getRuntime();
if (os.contains("win")) { if (os.contains("win")) {
// For Windows // For Windows
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url); SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
@ -181,17 +184,6 @@ public class SPDFApplication {
log.info("Running configs {}", applicationProperties.toString()); log.info("Running configs {}", applicationProperties.toString());
} }
@Value("${server.port:8080}")
public void setServerPort(String port) {
if ("auto".equalsIgnoreCase(port)) {
// Use Spring Boot's automatic port assignment (server.port=0)
SPDFApplication.serverPortStatic =
"0"; // This will let Spring Boot assign an available port
} else {
SPDFApplication.serverPortStatic = port;
}
}
public static void setServerPortStatic(String port) { public static void setServerPortStatic(String port) {
if ("auto".equalsIgnoreCase(port)) { if ("auto".equalsIgnoreCase(port)) {
// Use Spring Boot's automatic port assignment (server.port=0) // Use Spring Boot's automatic port assignment (server.port=0)

View File

@ -14,21 +14,21 @@ import jakarta.servlet.http.HttpServletResponse;
public class CleanUrlInterceptor implements HandlerInterceptor { public class CleanUrlInterceptor implements HandlerInterceptor {
private static final List<String> ALLOWED_PARAMS = private static final List<String> ALLOWED_PARAMS =
Arrays.asList( Arrays.asList(
"lang", "lang",
"endpoint", "endpoint",
"endpoints", "endpoints",
"logout", "logout",
"error", "error",
"errorOAuth", "errorOAuth",
"file", "file",
"messageType", "messageType",
"infoMessage"); "infoMessage");
@Override @Override
public boolean preHandle( public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception { throws Exception {
String queryString = request.getQueryString(); String queryString = request.getQueryString();
if (queryString != null && !queryString.isEmpty()) { if (queryString != null && !queryString.isEmpty()) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
@ -69,15 +69,15 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
@Override @Override
public void postHandle( public void postHandle(
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
Object handler, Object handler,
ModelAndView modelAndView) {} ModelAndView modelAndView) {}
@Override @Override
public void afterCompletion( public void afterCompletion(
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
Object handler, Object handler,
Exception ex) {} Exception ex) {}
} }

View File

@ -39,14 +39,14 @@ public class EndpointInspector implements ApplicationListener<ContextRefreshedEv
private void discoverEndpoints() { private void discoverEndpoints() {
try { try {
Map<String, RequestMappingHandlerMapping> mappings = Map<String, RequestMappingHandlerMapping> mappings =
applicationContext.getBeansOfType(RequestMappingHandlerMapping.class); applicationContext.getBeansOfType(RequestMappingHandlerMapping.class);
for (Map.Entry<String, RequestMappingHandlerMapping> entry : mappings.entrySet()) { for (Map.Entry<String, RequestMappingHandlerMapping> entry : mappings.entrySet()) {
RequestMappingHandlerMapping mapping = entry.getValue(); RequestMappingHandlerMapping mapping = entry.getValue();
Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods(); Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods();
for (Map.Entry<RequestMappingInfo, HandlerMethod> handlerEntry : for (Map.Entry<RequestMappingInfo, HandlerMethod> handlerEntry :
handlerMethods.entrySet()) { handlerMethods.entrySet()) {
RequestMappingInfo mappingInfo = handlerEntry.getKey(); RequestMappingInfo mappingInfo = handlerEntry.getKey();
HandlerMethod handlerMethod = handlerEntry.getValue(); HandlerMethod handlerMethod = handlerEntry.getValue();
@ -105,7 +105,7 @@ public class EndpointInspector implements ApplicationListener<ContextRefreshedEv
String infoString = mappingInfo.toString(); String infoString = mappingInfo.toString();
if (infoString.contains("{")) { if (infoString.contains("{")) {
String patternsSection = String patternsSection =
infoString.substring(infoString.indexOf("{") + 1, infoString.indexOf("}")); infoString.substring(infoString.indexOf("{") + 1, infoString.indexOf("}"));
for (String pattern : patternsSection.split(",")) { for (String pattern : patternsSection.split(",")) {
pattern = pattern.trim(); pattern = pattern.trim();

View File

@ -18,8 +18,8 @@ public class EndpointInterceptor implements HandlerInterceptor {
@Override @Override
public boolean preHandle( public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception { throws Exception {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
boolean isEnabled; boolean isEnabled;

View File

@ -25,23 +25,23 @@ public class ExternalAppDepConfig {
private final Map<String, List<String>> commandToGroupMapping; private final Map<String, List<String>> commandToGroupMapping;
public ExternalAppDepConfig( public ExternalAppDepConfig(
EndpointConfiguration endpointConfiguration, RuntimePathConfig runtimePathConfig) { EndpointConfiguration endpointConfiguration, RuntimePathConfig runtimePathConfig) {
this.endpointConfiguration = endpointConfiguration; this.endpointConfiguration = endpointConfiguration;
weasyprintPath = runtimePathConfig.getWeasyPrintPath(); weasyprintPath = runtimePathConfig.getWeasyPrintPath();
unoconvPath = runtimePathConfig.getUnoConvertPath(); unoconvPath = runtimePathConfig.getUnoConvertPath();
commandToGroupMapping = commandToGroupMapping =
new HashMap<>() { new HashMap<>() {
{ {
put("soffice", List.of("LibreOffice")); put("soffice", List.of("LibreOffice"));
put(weasyprintPath, List.of("Weasyprint")); put(weasyprintPath, List.of("Weasyprint"));
put("pdftohtml", List.of("Pdftohtml")); put("pdftohtml", List.of("Pdftohtml"));
put(unoconvPath, List.of("Unoconvert")); put(unoconvPath, List.of("Unoconvert"));
put("qpdf", List.of("qpdf")); put("qpdf", List.of("qpdf"));
put("tesseract", List.of("tesseract")); put("tesseract", List.of("tesseract"));
} }
}; };
} }
private boolean isCommandAvailable(String command) { private boolean isCommandAvailable(String command) {
@ -63,8 +63,8 @@ public class ExternalAppDepConfig {
private List<String> getAffectedFeatures(String group) { private List<String> getAffectedFeatures(String group) {
return endpointConfiguration.getEndpointsForGroup(group).stream() return endpointConfiguration.getEndpointsForGroup(group).stream()
.map(endpoint -> formatEndpointAsFeature(endpoint)) .map(endpoint -> formatEndpointAsFeature(endpoint))
.toList(); .toList();
} }
private String formatEndpointAsFeature(String endpoint) { private String formatEndpointAsFeature(String endpoint) {
@ -72,8 +72,8 @@ public class ExternalAppDepConfig {
String feature = endpoint.replace("-", " ").replace("pdf", "PDF").replace("img", "image"); String feature = endpoint.replace("-", " ").replace("pdf", "PDF").replace("img", "image");
// Split into words and capitalize each word // Split into words and capitalize each word
return Arrays.stream(feature.split("\\s+")) return Arrays.stream(feature.split("\\s+"))
.map(word -> capitalizeWord(word)) .map(word -> capitalizeWord(word))
.collect(Collectors.joining(" ")); .collect(Collectors.joining(" "));
} }
private String capitalizeWord(String word) { private String capitalizeWord(String word) {
@ -95,12 +95,12 @@ public class ExternalAppDepConfig {
List<String> affectedFeatures = getAffectedFeatures(group); List<String> affectedFeatures = getAffectedFeatures(group);
endpointConfiguration.disableGroup(group); endpointConfiguration.disableGroup(group);
log.warn( log.warn(
"Missing dependency: {} - Disabling group: {} (Affected features: {})", "Missing dependency: {} - Disabling group: {} (Affected features: {})",
command, command,
group, group,
affectedFeatures != null && !affectedFeatures.isEmpty() affectedFeatures != null && !affectedFeatures.isEmpty()
? String.join(", ", affectedFeatures) ? String.join(", ", affectedFeatures)
: "unknown"); : "unknown");
} }
} }
} }
@ -123,9 +123,9 @@ public class ExternalAppDepConfig {
endpointConfiguration.disableGroup("Python"); endpointConfiguration.disableGroup("Python");
endpointConfiguration.disableGroup("OpenCV"); endpointConfiguration.disableGroup("OpenCV");
log.warn( log.warn(
"Missing dependency: Python - Disabling Python features: {} and OpenCV features: {}", "Missing dependency: Python - Disabling Python features: {} and OpenCV features: {}",
String.join(", ", pythonFeatures), String.join(", ", pythonFeatures),
String.join(", ", openCVFeatures)); String.join(", ", openCVFeatures));
} else { } else {
// If Python is available, check for OpenCV // If Python is available, check for OpenCV
try { try {
@ -141,16 +141,16 @@ public class ExternalAppDepConfig {
List<String> openCVFeatures = getAffectedFeatures("OpenCV"); List<String> openCVFeatures = getAffectedFeatures("OpenCV");
endpointConfiguration.disableGroup("OpenCV"); endpointConfiguration.disableGroup("OpenCV");
log.warn( log.warn(
"OpenCV not available in Python - Disabling OpenCV features: {}", "OpenCV not available in Python - Disabling OpenCV features: {}",
String.join(", ", openCVFeatures)); String.join(", ", openCVFeatures));
} }
} catch (Exception e) { } catch (Exception e) {
List<String> openCVFeatures = getAffectedFeatures("OpenCV"); List<String> openCVFeatures = getAffectedFeatures("OpenCV");
endpointConfiguration.disableGroup("OpenCV"); endpointConfiguration.disableGroup("OpenCV");
log.warn( log.warn(
"Error checking OpenCV: {} - Disabling OpenCV features: {}", "Error checking OpenCV: {} - Disabling OpenCV features: {}",
e.getMessage(), e.getMessage(),
String.join(", ", openCVFeatures)); String.join(", ", openCVFeatures));
} }
} }
endpointConfiguration.logDisabledEndpointsSummary(); endpointConfiguration.logDisabledEndpointsSummary();

View File

@ -59,7 +59,7 @@ public class InitialSetup {
public void initEnableCSRFSecurity() throws IOException { public void initEnableCSRFSecurity() throws IOException {
if (GeneralUtils.isVersionHigher( if (GeneralUtils.isVersionHigher(
"0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) { "0.46.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled(); Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled();
if (!csrf) { if (!csrf) {
GeneralUtils.saveKeyToSettings("security.csrfDisabled", false); GeneralUtils.saveKeyToSettings("security.csrfDisabled", false);

View File

@ -25,7 +25,11 @@ public class WebMvcConfig implements WebMvcConfigurer {
// Handler for external static resources // Handler for external static resources
registry.addResourceHandler("/**") registry.addResourceHandler("/**")
.addResourceLocations( .addResourceLocations(
"file:" + InstallationPathConfig.getStaticPath(), "classpath:/static/"); "file:" + InstallationPathConfig.getStaticPath(),
"classpath:/static/"
);
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/");
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/");
// .setCachePeriod(0); // Optional: disable caching // .setCachePeriod(0); // Optional: disable caching
} }
} }

View File

@ -12,7 +12,7 @@
// import jakarta.servlet.http.HttpServletResponse; // import jakarta.servlet.http.HttpServletResponse;
// import jakarta.servlet.http.HttpSession; // import jakarta.servlet.http.HttpSession;
// import lombok.extern.slf4j.Slf4j; // import lombok.extern.slf4j.Slf4j;
// import stirling.software.SPDF.utils.RequestUriUtils; // import stirling.software.common.util.RequestUriUtils;
// //
//// @Component //// @Component
// @Slf4j // @Slf4j

View File

@ -36,9 +36,9 @@ public class SettingsController {
public ResponseEntity<String> updateApiKey(@RequestBody Boolean enabled) throws IOException { public ResponseEntity<String> updateApiKey(@RequestBody Boolean enabled) throws IOException {
if (applicationProperties.getSystem().getEnableAnalytics() != null) { if (applicationProperties.getSystem().getEnableAnalytics() != null) {
return ResponseEntity.status(HttpStatus.ALREADY_REPORTED) return ResponseEntity.status(HttpStatus.ALREADY_REPORTED)
.body( .body(
"Setting has already been set, To adjust please edit " "Setting has already been set, To adjust please edit "
+ InstallationPathConfig.getSettingsPath()); + InstallationPathConfig.getSettingsPath());
} }
GeneralUtils.saveKeyToSettings("system.enableAnalytics", enabled); GeneralUtils.saveKeyToSettings("system.enableAnalytics", enabled);
applicationProperties.getSystem().setEnableAnalytics(enabled); applicationProperties.getSystem().setEnableAnalytics(enabled);

View File

@ -27,9 +27,9 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@RestController @RestController
@RequestMapping("/api/v1/general") @RequestMapping("/api/v1/general")

Some files were not shown because too many files have changed in this diff Show More