Compare commits

..
Author SHA1 Message Date
pixeebot[bot] 7f096297af Hardening suggestions for Stirling-PDF / multiFileAsync (#3923)
I've reviewed the recently opened PR ([3922 - Support multi-file async
job results and ZIP
extraction](https://github.com/Stirling-Tools/Stirling-PDF/pull/3922))
and have identified some area(s) that could benefit from additional
hardening measures.

These changes should help prevent potential security vulnerabilities and
improve overall code quality.

Thank you for your consideration!
🧚🤖  Powered by Pixeebot  

[Feedback](https://ask.pixee.ai/feedback) |
[Community](https://pixee-community.slack.com/signup#/domain-signup) |
[Docs](https://docs.pixee.ai/)
![](https://d1zaessa2hpsmj.cloudfront.net/pixel/v1/track?writeKey=2PI43jNm7atYvAuK7rJUz3Kcd6A&event=PR_HARDENING%7CStirling-Tools%2FStirling-PDF%7C624e04a783753f7728d85d32469b6a2b5d4b113f)

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2025-07-10 16:36:19 +01:00
Anthony Stirling 624e04a783 tests and formatting 2025-07-10 15:22:37 +01:00
Anthony Stirling bb9f1d4f8b extra fixes 2025-07-10 14:58:11 +01:00
Anthony StirlingandCopilot 0d967aa5ea Update common/src/main/java/stirling/software/common/service/TaskManager.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-10 14:55:49 +01:00
Anthony Stirling f19ff6691d cleanups 2025-07-10 14:34:43 +01:00
Anthony Stirling 98b4763c96 remove "legacy" 2025-07-10 14:30:22 +01:00
Anthony Stirling 2c9537f786 multi file async 2025-07-10 14:18:35 +01:00
Ludy d17d10b240 chore: update German translation add new keys to ignore list (#3917)
# 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

- [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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-10 10:44:41 +01:00
Ludy a959e6eb2e fix(css): correct selector for free text editor overlay in PDF viewer CSS (#3916)
# Description of Changes


**What was changed:**  

- The CSS selector in `viewer-redact.css` and `viewer.css` was updated
from
  ```.annotationEditorLayer freeTextEditor .overlay.enabled```  
  to  
  ```.annotationEditorLayer .freeTextEditor .overlay.enabled```  
to properly target the `.freeTextEditor` element under
`.annotationEditorLayer`.

**Why the change was made:**  

- The previous selector was missing the dot before `freeTextEditor`, so
the overlay for free-text annotations was not being displayed as
intended.

---

## 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-10 10:39:20 +01:00
吕舒君 74d3d7d2c7 Update messages_zh_CN.properties (#3912)
# Description of Changes

Improved Chinese translation.

---

## 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/devGuide/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/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [x] 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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

No UI Changes

### Testing (if applicable)

I think translation needs no testing.
2025-07-10 10:38:59 +01:00
tkymmm 6f982c4db4 Update messages_ja_JP.properties (#3906)
# 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-10 10:38:44 +01:00
EthanHealy01 ad52ef641f This PR makes the multi-tool usable when zoomed in or when the browser window is narrowed a lot. (#3897)
# Description of Changes

This PR fixes responsiveness issues with the multi-tool interface when
zooming/narrowing the screen, implementing a zoom-responsive sidebar and
resolving navbar scaling conflicts.

## What was changed

• **Zoom-Responsive Sidebar**: Converts horizontal bottom buttons to
vertical left sidebar at 175%+ zoom using CSS media queries.

• **Fixed Navbar Scaling Bug**: Prevents desktop zoom from triggering 3x
mobile navbar scaling by requiring actual mobile device detection.

• **DPR Detection**: Uses Device Pixel Ratio to distinguish window
narrowing (3rem buttons) vs browser zoom (2.5rem buttons)

## Why the change was made

- **Accessibility**: Users zooming for accessibility reasons were
getting poor UX with bottom floating buttons
- **Bug Fix**: Desktop zoom was incorrectly triggering mobile navbar
scaling (3x size), making the interface unusable
- **Multi-context Support**: Different users narrow windows for
split-screen work vs zoom for accessibility - each needs different icon
sizes

## 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-09 13:14:18 +01:00
Anthony StirlingandDario Ghunney Ware 69bbb12ece Survey change to reduce prompts to users (#3913)
# 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.

---------

Co-authored-by: Dario Ghunney Ware <dariogware@gmail.com>
2025-07-08 19:25:42 +01:00
Dario Ghunney WareandAnthony Stirling 2218f0bffa Fix for generateOpenApiDocs Task (#3911)
# Description of Changes

Fix for `generateOpenApiDocs` task

---

## 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2025-07-08 18:04:25 +01:00
Anthony Stirling 05c10d3a9f fix for #3907 resource override (#3910)
# 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-08 11:57:49 +01:00
Anthony Stirling e0746b468e Fix for security not downloading and running correctly in V1.0.0 (#3902)
# 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 23:54:58 +01:00
stirlingbot[bot] 721cb8e0a3 Update 3rd Party Licenses (#3893)
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-07-07 22:33:30 +01:00
StepSecurity Bot 6496015af7 [StepSecurity] ci: Harden GitHub Actions (#3901)
## Summary

This pull request is created by
[StepSecurity](https://app.stepsecurity.io/securerepo) at the request of
@Ludy87. Please merge the Pull Request to incorporate the requested
changes. Please tag @Ludy87 on your message if you have any questions
related to the PR.
## Security Fixes

### Pinned Dependencies

GitHub Action tags and Docker tags are mutable. This poses a security
risk. GitHub's Security Hardening guide recommends pinning actions to
full length commit.

- [GitHub Security
Guide](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions)
- [The Open Source Security Foundation (OpenSSF) Security
Guide](https://github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies)


## Feedback
For bug reports, feature requests, and general feedback; please email
support@stepsecurity.io. To create such PRs, please visit
https://app.stepsecurity.io/securerepo.


Signed-off-by: StepSecurity Bot <bot@stepsecurity.io>

Signed-off-by: StepSecurity Bot <bot@stepsecurity.io>
2025-07-07 22:21:58 +01:00
Anthony Stirling df4539dc35 Fix release generation issues (#3898)
# 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 17:45:48 +01:00
Anthony Stirling baeb1acb8c Update PR-Demo-Comment-with-react.yml (#3894)
# 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/devGuide/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/devGuide/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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 15:17:31 +01:00
Dario Ghunney Ware d80bcfe970 Jpackage Input Path Fix (#3892)
Corrected input path for jpackage plugin

---

## 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/devGuide/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/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [x] 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/devGuide/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/devGuide/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 14:37:35 +01:00
stirlingbot[bot] 296758f51b 🌐 Sync Translations + Update README Progress Table (#3890)
### 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-07-07 10:34:47 +01:00
LudyandCopilot 141320cadd chore(i18n): update German translations for de_DE locale (#3884)
# Description of Changes

- **What was changed**  
- Updated `messages_de_DE.properties` to correct spacing in prompts
(e.g., changed `PDFs auswählen(2+)` to `PDFs auswählen (2+)`).
- Translated numerous English strings (error messages, prompts, UI
labels) into German.
- Standardized punctuation, grammar, and terminology across the German
locale.
- Fixed minor typos and formatting issues (e.g., “Stampelbild” →
“Stempelbild”, pipeline scan help, audit dashboard labels).

---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-07 10:27:06 +01:00
stirlingbot[bot] 7fa5e130d9 🤖 format everything with pre-commit by stirlingbot (#3882)
Auto-generated by [create-pull-request][1] with **stirlingbot**

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

Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com>
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-07-07 10:05:50 +01:00
Ludy b4f8b896d1 deps: update Python requirements and add --strip-extras flag (#3887)
# Description of Changes

- **What was changed**  
  - Added the `--strip-extras` option to the `pip-compile` commands in  
    `.github/scripts/requirements_pre_commit.txt` and  
    `.github/scripts/requirements_sync_readme.txt`.  
  - Bumped versions of CI and development dependencies:  
- In `.github/scripts`: `filelock`, `identify`, `platformdirs`,
`pre-commit`, `tomlkit`
- In `testing/cucumber/requirements.txt`: `certifi`,
`charset-normalizer`, `pillow`, `pycryptodome`, `pypdf`, `reportlab`,
`typing-extensions`, and others
  - Regenerated hashes for all updated packages.
- Added new batch script `scripts/generate_requirements.bat` to automate
requirement regeneration

- **Why the change was made**  
- Keep dependencies up-to-date with latest patch releases to address
security fixes and compatibility improvements.
- Reduce lockfile size and remove unneeded extras by stripping optional
markers.
- Ensure CI and test environments use the most current, secure versions
of tooling.

---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 10:05:23 +01:00
Ludy 4e483ca5c9 fix(css): correct spelling of separator in navbar and theme CSS (#3888)
# Description of Changes

**What was changed**
- Corrected the spelling of the CSS variable
`--md-nav-color-on-separator` (previously misspelled as
`--md-nav-color-on-seperator`) across `navbar.css`, `theme.dark.css`,
and `theme.light.css`. Adjusted related CSS comments and formatting for
consistency. Updated HTML templates (`fragments/navbar.html` and
`home.html`) to reference the corrected variable.

**Why the change was made**
- To resolve styling inconsistencies and prevent potential runtime
errors caused by the typo.

---

## 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)
- [ ] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 10:02:59 +01:00
Ludy 6cc42ff6d6 fix(spelling): correct selectFilter typo across templates and localization files (#3886)
# Description of Changes

- **What was changed**  
- Renamed all occurrences of the misspelled key `selectFillter` to
`selectFilter` in HTML templates (`team-details.html`,
`adminSettings.html`, `cert-sign.html`) and in every locale properties
file under `stirling-pdf/src/main/resources/messages_*.properties`.
- Removed obsolete `WorkInProgess` entries from various locale files.

- **Why the change was made**  
To fix a consistent spelling mistake in the UI select placeholder key,
ensuring that the label displays correctly and translations resolve
properly.

---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 09:50:40 +01:00
Ludy eb7f5e57cc docs: restructure documentation paths, update PR template links, and add exception handling guide (#3885)
# Description of Changes

- **What was changed**  
- Updated `.github/labeler-config-srvaroa.yml` to match the new
`devGuide/.*` patterns instead of the old `HowToAddNewLanguage.md`
- Modified `.github/pull_request_template.md` to point to
`devGuide/DeveloperGuide.md` and `devGuide/HowToAddNewLanguage.md` for
the Developer Guide and translation-tags sections
- Updated the SSO link in `README.md` to point to the 'Single Sign-On
Configuration' section on docs.stirlingpdf.com instead of the outdated
Enterprise Edition page
- Changed the relative link in `devGuide/DeveloperGuide.md` to use
`../CONTRIBUTING.md` for consistency
- Added a new `devGuide/EXCEPTION_HANDLING_GUIDE.md` to document
standard exception-handling patterns and internationalisation best
practices
- Updated `devGuide/README.md` index to reflect the new location and
naming conventions for development guides

- **Why the change was made**  
- To centralise all development documentation under a single `devGuide`
directory for better organisation and discoverability.
- To ensure that GitHub labeler rules and PR templates correctly
reference the new file structure.
- To introduce a clear, project-wide guide on exception handling,
improving consistency across Java, JavaScript, HTML/CSS, and Python
components.

Closes #3799

---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-07-07 09:49:44 +01:00
14a4bdfb1b Exception cleanup, resource change and OCR Defaults (#3876)
# 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.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: a <a>
2025-07-05 17:33:25 +01:00
Reece BrowneandCopilot b4a7b5d520 dropdown fixes (#3879)
# 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.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-05 16:01:40 +01:00
stirlingbot[bot] 446a309450 🌐 Sync Translations + Update README Progress Table (#3875)
### 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-07-04 19:47:58 +01:00
albanobattistella 25c9504fed Update messages_it_IT.properties (#3871)
# 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

- [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)

- [ ] 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-07-04 19:01:30 +01:00
Reece BrowneandCopilot 0a402e562b Scaling fixes (#3868)
# 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.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-04 18:58:57 +01:00
Anthony Stirling 6158454020 Update push-docker.yml 2025-07-03 20:20:23 +01:00
Anthony Stirling 6cc0303873 Update push-docker.yml to change alpha to be fat (#3872)
# 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-07-03 20:10:46 +01:00
stirlingbot[bot] e865de8544 🌐 Sync Translations + Update README Progress Table (#3869)
### 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-07-03 12:01:53 +01:00
Balázs SzücsandCopilot 33d2ca2150 Updated Hungarian translations for error messages, and other miscellaneous items (#3863)
This pull request adds accurate translations, and improve clarity of
exisiting translations, also updates the ignore translations list.

### Localization Improvements:

* Updated Hungarian translations for error messages to ensure clarity
and consistency, such as correcting "Invalid role" to "Érvénytelen
szerepkör" and refining descriptions for corrupted PDF files and
encryption issues.
* Improved Hungarian translations for UI labels and prompts, including
"Pro Features" changed to "Pro Funkciók" and "Team management features
require a Pro licence or higher" updated to "A csapatkezelési
funkciókhoz Pro licenc vagy magasabb szintű licenc szükséges."


---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [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-07-03 12:00:45 +01:00
GeistFighter c97c565684 Updated the French translation file messages_fr_FR.properties (#3864)
Updated the French translation file (messages_fr_FR.properties):

- Translated previously untranslated keys
- Reviewed and corrected existing translations for consistency and
clarity
- Maintained comment lines untouched

This update improves the user interface for French-speaking users and
ensures more accurate localization across the application.

---

Question: How can I include this updated language file as a custom file
in a Docker setup for Stirling PDF (e.g. via a config customFiles/xxxx
)? Any guidance or examples would be appreciated. Thanks.

## 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

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

- [ ] 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-07-03 12:00:10 +01:00
Anthony Stirlingandpixeebot[bot] 7617e5176b exception handling error to warn etc (#3866)
# 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.

---------

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2025-07-03 11:36:06 +01:00
stirlingbot[bot] a95743286e 🌐 Sync Translations + Update README Progress Table (#3862)
### 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-07-02 16:55:34 +01:00
albanobattistella 1487bd707f Update messages_it_IT.properties (#3857)
# 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-07-02 16:53:11 +01:00
Reece BrowneandAnthony Stirling c0bbd92c9e remove footer gap (#3855)
# 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.

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2025-07-02 16:52:52 +01:00
Anthony Stirlinganda dc6823d7ba exception handling and exception improvements (#3858)
# Description of Changes

This pull request introduces several improvements to enhance error
handling, internationalization, and documentation in the codebase. The
key changes include the addition of `ExceptionUtils` and `I18nUtils`
utility classes for consistent exception handling and internationalized
messages, updates to documentation paths, and modifications to existing
methods to integrate the new utilities.

### Error Handling Enhancements:
* **Added `ExceptionUtils` utility class**: Provides standardized
methods for creating and handling exceptions with internationalized
error messages, including specific handling for PDF corruption,
encryption issues, and other file-related errors.
* **Integrated `ExceptionUtils` into `CustomPDFDocumentFactory`**:
Updated `loadFromFile` and `loadFromBytes` methods to log and handle
exceptions using `ExceptionUtils`, ensuring consistent error handling
across PDF operations.
[[1]](diffhunk://#diff-10208c1fc2e04631a8cf2a2a99b2a1160e532e75a7b840ad752f3b0130b89851R358-R363)
[[2]](diffhunk://#diff-10208c1fc2e04631a8cf2a2a99b2a1160e532e75a7b840ad752f3b0130b89851R375-R381)
* **Updated `FileToPdf` to use `ExceptionUtils`**: Replaced direct
exception throwing with `ExceptionUtils.createHtmlFileRequiredException`
for unsupported file formats.

### Internationalization Improvements:
* **Added `I18nUtils` utility class**: Centralized access to Spring's
`MessageSource` for retrieving localized messages, enabling consistent
internationalization across the application.

### Documentation Updates:
* **Updated documentation paths in `CONTRIBUTING.md` and `README.md`**:
Changed paths to reference the new `devGuide` folder for developer
documentation and translation guides.
[[1]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L28-R28)
[[2]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L40-R51)
[[3]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L171-L174)
---

## 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.

---------

Co-authored-by: a <a>
2025-07-02 16:51:45 +01:00
stirlingbot[bot] 5cd76b51d9 🌐 Sync Translations + Update README Progress Table (#3851)
### 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-07-01 18:22:01 +01:00
Balázs Szücs 8f2e6bc77a Updated Hungarian translations for add attachments feature (#3853)
> ### Copilot generated summary:
> 
> This pull request updates Hungarian translations in the
`messages_hu_HU.properties` file to improve clarity and consistency. The
changes primarily focus on refining text for better understanding and
alignment with the intended functionality.
> 
> 
> ### Translation improvements:
> 
> * Revised multiple entries under the `attachments` section to provide
more accurate translations for adding attachments to PDFs, including the
title, header, description, placeholder text, and button label.

### Picture of translation


![image](https://github.com/user-attachments/assets/07c3ce47-922a-4bd7-a292-625dc773b8de)


---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [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.
2025-07-01 16:15:10 +01:00
stirlingbot[bot] 7fb6e79316 Update 3rd Party Licenses (#3852)
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-07-01 15:55:47 +01:00
dependabot[bot] 7dd6d50ef3 Bump urllib3 from 2.3.0 to 2.5.0 in /testing/cucumber in the pip group across 1 directory (#3769)
Bumps the pip group with 1 update in the /testing/cucumber directory:
[urllib3](https://github.com/urllib3/urllib3).

Updates `urllib3` from 2.3.0 to 2.5.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/releases">urllib3's
releases</a>.</em></p>
<blockquote>
<h2>2.5.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h1>Security issues</h1>
<p>urllib3 2.5.0 fixes two moderate security issues:</p>
<ul>
<li>Pool managers now properly control redirects when
<code>retries</code> is passed — CVE-2025-50181 reported by <a
href="https://github.com/sandumjacob"><code>@​sandumjacob</code></a>
(5.3 Medium, GHSA-pq67-6m6q-mj2v)</li>
<li>Redirects are now controlled by urllib3 in the Node.js runtime —
CVE-2025-50182 (5.3 Medium, GHSA-48p4-8xcf-vxj5)</li>
</ul>
<h1>Features</h1>
<ul>
<li>Added support for the <code>compression.zstd</code> module that is
new in Python 3.14. See <a href="https://peps.python.org/pep-0784/">PEP
784</a> for more information. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3610">#3610</a>)</li>
<li>Added support for version 0.5 of <code>hatch-vcs</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3612">#3612</a>)</li>
</ul>
<h1>Bugfixes</h1>
<ul>
<li>Raised exception for <code>HTTPResponse.shutdown</code> on a
connection already released to the pool. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3581">#3581</a>)</li>
<li>Fixed incorrect <code>CONNECT</code> statement when using an IPv6
proxy with <code>connection_from_host</code>. Previously would not be
wrapped in <code>[]</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3615">#3615</a>)</li>
</ul>
<h2>2.4.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h1>Features</h1>
<ul>
<li>Applied PEP 639 by specifying the license fields in pyproject.toml.
(<a
href="https://redirect.github.com/urllib3/urllib3/issues/3522">#3522</a>)</li>
<li>Updated exceptions to save and restore more properties during the
pickle/serialization process. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3567">#3567</a>)</li>
<li>Added <code>verify_flags</code> option to
<code>create_urllib3_context</code> with a default of
<code>VERIFY_X509_PARTIAL_CHAIN</code> and
<code>VERIFY_X509_STRICT</code> for Python 3.13+. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3571">#3571</a>)</li>
</ul>
<h1>Bugfixes</h1>
<ul>
<li>Fixed a bug with partial reads of streaming data in Emscripten. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3555">#3555</a>)</li>
</ul>
<h1>Misc</h1>
<ul>
<li>Switched to uv for installing development dependecies. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3550">#3550</a>)</li>
<li>Removed the <code>multiple.intoto.jsonl</code> asset from GitHub
releases. Attestation of release files since v2.3.0 can be found on
PyPI. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3566">#3566</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's
changelog</a>.</em></p>
<blockquote>
<h1>2.5.0 (2025-06-18)</h1>
<h2>Features</h2>
<ul>
<li>Added support for the <code>compression.zstd</code> module that is
new in Python 3.14.
See <code>PEP 784 &lt;https://peps.python.org/pep-0784/&gt;</code>_ for
more information.
(<code>[#3610](https://github.com/urllib3/urllib3/issues/3610)
&lt;https://github.com/urllib3/urllib3/issues/3610&gt;</code>__)</li>
<li>Added support for version 0.5 of <code>hatch-vcs</code>
(<code>[#3612](https://github.com/urllib3/urllib3/issues/3612)
&lt;https://github.com/urllib3/urllib3/issues/3612&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a security issue where restricting the maximum number of
followed
redirects at the <code>urllib3.PoolManager</code> level via the
<code>retries</code> parameter
did not work.</li>
<li>Made the Node.js runtime respect redirect parameters such as
<code>retries</code>
and <code>redirects</code>.</li>
<li>Raised exception for <code>HTTPResponse.shutdown</code> on a
connection already released to the pool.
(<code>[#3581](https://github.com/urllib3/urllib3/issues/3581)
&lt;https://github.com/urllib3/urllib3/issues/3581&gt;</code>__)</li>
<li>Fixed incorrect <code>CONNECT</code> statement when using an IPv6
proxy with <code>connection_from_host</code>. Previously would not be
wrapped in <code>[]</code>.
(<code>[#3615](https://github.com/urllib3/urllib3/issues/3615)
&lt;https://github.com/urllib3/urllib3/issues/3615&gt;</code>__)</li>
</ul>
<h1>2.4.0 (2025-04-10)</h1>
<h2>Features</h2>
<ul>
<li>Applied PEP 639 by specifying the license fields in pyproject.toml.
(<code>[#3522](https://github.com/urllib3/urllib3/issues/3522)
&lt;https://github.com/urllib3/urllib3/issues/3522&gt;</code>__)</li>
<li>Updated exceptions to save and restore more properties during the
pickle/serialization process.
(<code>[#3567](https://github.com/urllib3/urllib3/issues/3567)
&lt;https://github.com/urllib3/urllib3/issues/3567&gt;</code>__)</li>
<li>Added <code>verify_flags</code> option to
<code>create_urllib3_context</code> with a default of
<code>VERIFY_X509_PARTIAL_CHAIN</code> and
<code>VERIFY_X509_STRICT</code> for Python 3.13+.
(<code>[#3571](https://github.com/urllib3/urllib3/issues/3571)
&lt;https://github.com/urllib3/urllib3/issues/3571&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug with partial reads of streaming data in Emscripten.
(<code>[#3555](https://github.com/urllib3/urllib3/issues/3555)
&lt;https://github.com/urllib3/urllib3/issues/3555&gt;</code>__)</li>
</ul>
<h2>Misc</h2>
<ul>
<li>Switched to uv for installing development dependecies.
(<code>[#3550](https://github.com/urllib3/urllib3/issues/3550)
&lt;https://github.com/urllib3/urllib3/issues/3550&gt;</code>__)</li>
<li>Removed the <code>multiple.intoto.jsonl</code> asset from GitHub
releases. Attestation of release files since v2.3.0 can be found on
PyPI. (<code>[#3566](https://github.com/urllib3/urllib3/issues/3566)
&lt;https://github.com/urllib3/urllib3/issues/3566&gt;</code>__)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/urllib3/urllib3/commit/aaab4eccc10c965897540b21e15f11859d0b62e7"><code>aaab4ec</code></a>
Release 2.5.0</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/7eb4a2aafe49a279c29b6d1f0ed0f42e9736194f"><code>7eb4a2a</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/f05b1329126d5be6de501f9d1e3e36738bc08857"><code>f05b132</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/d03fe327a71d09728512217149f269763671f296"><code>d03fe32</code></a>
Fix HTTP tunneling with IPv6 in older Python versions</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/11661e9bb4278e43d081f47a516e287a928c2206"><code>11661e9</code></a>
Bump github/codeql-action from 3.28.0 to 3.29.0 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3624">#3624</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/6a0ecc6b16fe30f721021b44a81d19615098c71e"><code>6a0ecc6</code></a>
Update v2 migration guide to 2.4.0 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3621">#3621</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/8e32e60d9024c05bc6f7adda08bdf6c539d0b0d4"><code>8e32e60</code></a>
Raise exception for shutdown on a connection already released to the
pool (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3">#3</a>...</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/9996e0fbf90b77083ad3c73737a6c6395703faa9"><code>9996e0f</code></a>
Fix emscripten CI for Chrome 137+ (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3599">#3599</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/4fd1a99a59725faf0efc946ce3b6bc9a194420af"><code>4fd1a99</code></a>
Bump RECENT_DATE (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3617">#3617</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/c4b5917e911a90c8bf279448df8952a682294135"><code>c4b5917</code></a>
Add support for the new <code>compression.zstd</code> module in Python
3.14 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3611">#3611</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/urllib3/urllib3/compare/2.3.0...2.5.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=pip&previous-version=2.3.0&new-version=2.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/Stirling-Tools/Stirling-PDF/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 15:53:03 +01:00
dependabot[bot] e1e295d33b Bump com.opencsv:opencsv from 5.11.1 to 5.11.2 (#3793)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps com.opencsv:opencsv from 5.11.1 to 5.11.2.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.opencsv:opencsv&package-manager=gradle&previous-version=5.11.1&new-version=5.11.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 15:52:13 +01:00
dependabot[bot] 09b185febf Bump commonmarkVersion from 0.24.0 to 0.25.0 (#3789)
Bumps `commonmarkVersion` from 0.24.0 to 0.25.0.
Updates `org.commonmark:commonmark` from 0.24.0 to 0.25.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/commonmark/commonmark-java/releases">org.commonmark:commonmark's
releases</a>.</em></p>
<blockquote>
<h2>commonmark-java 0.25.0</h2>
<h3>Added</h3>
<ul>
<li>Include OSGi metadata in jars (<code>META-INF/MANIFEST.MF</code>
files) (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/378">#378</a>)</li>
<li>More documentation with examples for <code>Node</code> classes (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/370">#370</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>GitHub tables: Tables are now parsed even if there's no blank line
before the
table heading, matching GitHub's behavior. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/381">#381</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>MarkdownRenderer</code>: Fix precedence for
<code>nodeRendererFactory</code>: Factories passed
to the builder can now override rendering for core node types. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/368">#368</a>)</li>
<li><code>MarkdownRenderer</code>: Fix exception with ordered lists with
a long first number
followed by a shorter one (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/382">#382</a>)</li>
<li>Fix warning in Eclipse about &quot;missing 'requires
transitive'&quot; (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/358">#358</a>)</li>
<li>Fix Android incompatibility with <code>requireNonNullElseGet</code>
(<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/369">#369</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md">org.commonmark:commonmark's
changelog</a>.</em></p>
<blockquote>
<h2>[0.25.0] - 2025-06-20</h2>
<h3>Added</h3>
<ul>
<li>Include OSGi metadata in jars (<code>META-INF/MANIFEST.MF</code>
files) (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/378">#378</a>)</li>
<li>More documentation with examples for <code>Node</code> classes (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/370">#370</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>GitHub tables: Tables are now parsed even if there's no blank line
before the
table heading, matching GitHub's behavior. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/381">#381</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>MarkdownRenderer</code>: Fix precedence for
<code>nodeRendererFactory</code>: Factories passed
to the builder can now override rendering for core node types. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/368">#368</a>)</li>
<li><code>MarkdownRenderer</code>: Fix exception with ordered lists with
a long first number
followed by a shorter one (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/382">#382</a>)</li>
<li>Fix warning in Eclipse about &quot;missing 'requires
transitive'&quot; (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/358">#358</a>)</li>
<li>Fix Android incompatibility with <code>requireNonNullElseGet</code>
(<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/369">#369</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/99e6050ae12e862e39f4a0b81108ec155d4817af"><code>99e6050</code></a>
[maven-release-plugin] prepare release commonmark-parent-0.25.0</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/17019911385988005a2083d74df8952ce045a6db"><code>1701991</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/385">#385</a>
from commonmark/release-0.25</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/6354f0244746a5e6cd6b44f0be61e153681f2270"><code>6354f02</code></a>
Prepare for version 0.25.0</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/adcf2f232c9fcf55e29770c78a9285d5446517f3"><code>adcf2f2</code></a>
Prepare CHANGELOG for release 0.25</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/8cc1fa20723736833441b9e9ecee93a1df1045fb"><code>8cc1fa2</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/384">#384</a>
from commonmark/central-publisher-portal</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/5e6b740271587bb0881a5f065d56f05f8ae7ed9f"><code>5e6b740</code></a>
Migrate from OSSRH to Central Publisher Portal</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/7616e19533837233fa4dfd2b9f09e283d6f00927"><code>7616e19</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/383">#383</a>
from commonmark/markdown-renderer-ordered-list-fix</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/971aaa9edeaa7ef161e4553b001be91bf2d83cea"><code>971aaa9</code></a>
MarkdownRenderer: Fix ordered list with long start number (fixes <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/382">#382</a>)</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/32c9290bc4c9b7d9daec25832dec31404459f8e4"><code>32c9290</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/381">#381</a>
from commonmark/tables-without-blank-line-before</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/bb57a5f0921ab7e46f44128c875720617452564a"><code>bb57a5f</code></a>
Parse tables without blank line before</li>
<li>Additional commits viewable in <a
href="https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...commonmark-parent-0.25.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.commonmark:commonmark-ext-gfm-tables` from 0.24.0 to 0.25.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/commonmark/commonmark-java/releases">org.commonmark:commonmark-ext-gfm-tables's
releases</a>.</em></p>
<blockquote>
<h2>commonmark-java 0.25.0</h2>
<h3>Added</h3>
<ul>
<li>Include OSGi metadata in jars (<code>META-INF/MANIFEST.MF</code>
files) (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/378">#378</a>)</li>
<li>More documentation with examples for <code>Node</code> classes (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/370">#370</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>GitHub tables: Tables are now parsed even if there's no blank line
before the
table heading, matching GitHub's behavior. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/381">#381</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>MarkdownRenderer</code>: Fix precedence for
<code>nodeRendererFactory</code>: Factories passed
to the builder can now override rendering for core node types. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/368">#368</a>)</li>
<li><code>MarkdownRenderer</code>: Fix exception with ordered lists with
a long first number
followed by a shorter one (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/382">#382</a>)</li>
<li>Fix warning in Eclipse about &quot;missing 'requires
transitive'&quot; (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/358">#358</a>)</li>
<li>Fix Android incompatibility with <code>requireNonNullElseGet</code>
(<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/369">#369</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md">org.commonmark:commonmark-ext-gfm-tables's
changelog</a>.</em></p>
<blockquote>
<h2>[0.25.0] - 2025-06-20</h2>
<h3>Added</h3>
<ul>
<li>Include OSGi metadata in jars (<code>META-INF/MANIFEST.MF</code>
files) (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/378">#378</a>)</li>
<li>More documentation with examples for <code>Node</code> classes (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/370">#370</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>GitHub tables: Tables are now parsed even if there's no blank line
before the
table heading, matching GitHub's behavior. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/381">#381</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>MarkdownRenderer</code>: Fix precedence for
<code>nodeRendererFactory</code>: Factories passed
to the builder can now override rendering for core node types. (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/368">#368</a>)</li>
<li><code>MarkdownRenderer</code>: Fix exception with ordered lists with
a long first number
followed by a shorter one (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/382">#382</a>)</li>
<li>Fix warning in Eclipse about &quot;missing 'requires
transitive'&quot; (<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/358">#358</a>)</li>
<li>Fix Android incompatibility with <code>requireNonNullElseGet</code>
(<a
href="https://redirect.github.com/commonmark/commonmark-java/issues/369">#369</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/99e6050ae12e862e39f4a0b81108ec155d4817af"><code>99e6050</code></a>
[maven-release-plugin] prepare release commonmark-parent-0.25.0</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/17019911385988005a2083d74df8952ce045a6db"><code>1701991</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/385">#385</a>
from commonmark/release-0.25</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/6354f0244746a5e6cd6b44f0be61e153681f2270"><code>6354f02</code></a>
Prepare for version 0.25.0</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/adcf2f232c9fcf55e29770c78a9285d5446517f3"><code>adcf2f2</code></a>
Prepare CHANGELOG for release 0.25</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/8cc1fa20723736833441b9e9ecee93a1df1045fb"><code>8cc1fa2</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/384">#384</a>
from commonmark/central-publisher-portal</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/5e6b740271587bb0881a5f065d56f05f8ae7ed9f"><code>5e6b740</code></a>
Migrate from OSSRH to Central Publisher Portal</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/7616e19533837233fa4dfd2b9f09e283d6f00927"><code>7616e19</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/383">#383</a>
from commonmark/markdown-renderer-ordered-list-fix</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/971aaa9edeaa7ef161e4553b001be91bf2d83cea"><code>971aaa9</code></a>
MarkdownRenderer: Fix ordered list with long start number (fixes <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/382">#382</a>)</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/32c9290bc4c9b7d9daec25832dec31404459f8e4"><code>32c9290</code></a>
Merge pull request <a
href="https://redirect.github.com/commonmark/commonmark-java/issues/381">#381</a>
from commonmark/tables-without-blank-line-before</li>
<li><a
href="https://github.com/commonmark/commonmark-java/commit/bb57a5f0921ab7e46f44128c875720617452564a"><code>bb57a5f</code></a>
Parse tables without blank line before</li>
<li>Additional commits viewable in <a
href="https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.24.0...commonmark-parent-0.25.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 15:51:52 +01:00
dependabot[bot] 9760c30638 Bump io.swagger.core.v3:swagger-core-jakarta from 2.2.33 to 2.2.34 (#3791)
Bumps io.swagger.core.v3:swagger-core-jakarta from 2.2.33 to 2.2.34.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=io.swagger.core.v3:swagger-core-jakarta&package-manager=gradle&previous-version=2.2.33&new-version=2.2.34)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 15:51:38 +01:00
dependabot[bot]andLudy afb1df02b6 Bump org.panteleyev.jpackageplugin from 1.6.1 to 1.7.3 (#3740)
Bumps org.panteleyev.jpackageplugin from 1.6.1 to 1.7.3.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.panteleyev.jpackageplugin&package-manager=gradle&previous-version=1.6.1&new-version=1.7.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ludy <Ludy87@users.noreply.github.com>
2025-07-01 15:51:26 +01:00
Ludy fd10e5db10 chore: include ignore_translation.toml in README sync commit (#3845)
# Description of Changes

- Updated the GitHub Actions workflow `.github/workflows/sync_files.yml`
to also include `scripts/ignore_translation.toml` in the automatic
commit step.
- This TOML file lists translatable strings that should be excluded
because they are explicitly marked as non-translatable.
- Including it in the sync ensures that cleanup of such excluded strings
is versioned together with README changes, improving consistency of
translation tooling automation.

---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-07-01 15:49:58 +01:00
stirlingbot[bot] 14d4fbb2a3 🤖 format everything with pre-commit by stirlingbot (#3804)
Auto-generated by [create-pull-request][1] with **stirlingbot**

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

Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com>
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-07-01 15:41:23 +01:00
stirlingbot[bot] 9eb534a6d2 🌐 Sync Translations + Update README Progress Table (#3807)
### 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-07-01 15:41:06 +01:00
dependabot[bot] 0b342d463c chore(deps): bump sigstore/cosign-installer from 3.9.0 to 3.9.1 (#3801)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps
[sigstore/cosign-installer](https://github.com/sigstore/cosign-installer)
from 3.9.0 to 3.9.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sigstore/cosign-installer/releases">sigstore/cosign-installer's
releases</a>.</em></p>
<blockquote>
<h2>v3.9.1</h2>
<h2>What's Changed</h2>
<ul>
<li>default action install to use release v2.5.1 by <a
href="https://github.com/cpanato"><code>@​cpanato</code></a> in <a
href="https://redirect.github.com/sigstore/cosign-installer/pull/193">sigstore/cosign-installer#193</a></li>
<li>default cosign to v2.5.2 by <a
href="https://github.com/cpanato"><code>@​cpanato</code></a> in <a
href="https://redirect.github.com/sigstore/cosign-installer/pull/194">sigstore/cosign-installer#194</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/sigstore/cosign-installer/compare/v3.9.0...v3.9.1">https://github.com/sigstore/cosign-installer/compare/v3.9.0...v3.9.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/sigstore/cosign-installer/commit/398d4b0eeef1380460a10c8013a76f728fb906ac"><code>398d4b0</code></a>
default cosign to v2.5.2 (<a
href="https://redirect.github.com/sigstore/cosign-installer/issues/194">#194</a>)</li>
<li><a
href="https://github.com/sigstore/cosign-installer/commit/84f54a2bcd1ecf70e51a05388183dce4e1487230"><code>84f54a2</code></a>
default action install to use release v2.5.1 (<a
href="https://redirect.github.com/sigstore/cosign-installer/issues/193">#193</a>)</li>
<li>See full diff in <a
href="https://github.com/sigstore/cosign-installer/compare/fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3...398d4b0eeef1380460a10c8013a76f728fb906ac">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sigstore/cosign-installer&package-manager=github_actions&previous-version=3.9.0&new-version=3.9.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 15:40:09 +01:00
dependabot[bot] e7ac4b7b20 chore(deps): bump step-security/harden-runner from 2.12.1 to 2.12.2 (#3849)
Bumps
[step-security/harden-runner](https://github.com/step-security/harden-runner)
from 2.12.1 to 2.12.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/step-security/harden-runner/releases">step-security/harden-runner's
releases</a>.</em></p>
<blockquote>
<h2>v2.12.2</h2>
<h2>What's Changed</h2>
<p>Added HTTPS Monitoring for additional destinations -
*.githubusercontent.com
Bug fixes:</p>
<ul>
<li>Implicitly allow local multicast, local unicast and broadcast IP
addresses in block mode</li>
<li>Increased policy map size for block mode</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/step-security/harden-runner/compare/v2...v2.12.2">https://github.com/step-security/harden-runner/compare/v2...v2.12.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/step-security/harden-runner/commit/6c439dc8bdf85cadbbce9ed30d1c7b959517bc49"><code>6c439dc</code></a>
Merge pull request <a
href="https://redirect.github.com/step-security/harden-runner/issues/562">#562</a>
from step-security/rc-22</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/bf5688696d0b2cf8221eadb38e4232386015763a"><code>bf56886</code></a>
update agent</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/5436dac7b5fa76a1a179168f5f4de86c00e22c84"><code>5436dac</code></a>
update agent</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/88d305a3530acfa6d1939000baaa571e520df9c8"><code>88d305a</code></a>
update agent</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/b976878278dbe3bc16039f7165b8faf809c50297"><code>b976878</code></a>
update agent</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/875cc92db280a03598e7492a3e6c165c689f7af6"><code>875cc92</code></a>
Update agent</li>
<li>See full diff in <a
href="https://github.com/step-security/harden-runner/compare/002fdce3c6a235733a90a27c80493a3241e56863...6c439dc8bdf85cadbbce9ed30d1c7b959517bc49">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=step-security/harden-runner&package-manager=github_actions&previous-version=2.12.1&new-version=2.12.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 15:39:12 +01:00
dependabot[bot] 48c0112b87 chore(deps): bump github/codeql-action from 3.29.0 to 3.29.2 (#3848)
Bumps [github/codeql-action](https://github.com/github/codeql-action)
from 3.29.0 to 3.29.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/releases">github/codeql-action's
releases</a>.</em></p>
<blockquote>
<h2>v3.29.2</h2>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>3.29.2 - 30 Jun 2025</h2>
<ul>
<li>Experimental: When the <code>quality-queries</code> input for the
<code>init</code> action is provided with an argument, separate
<code>.quality.sarif</code> files are produced and uploaded for each
language with the results of the specified queries. Do not use this in
production as it is part of an internal experiment and subject to change
at any time. <a
href="https://redirect.github.com/github/codeql-action/pull/2935">#2935</a></li>
</ul>
<p>See the full <a
href="https://github.com/github/codeql-action/blob/v3.29.2/CHANGELOG.md">CHANGELOG.md</a>
for more information.</p>
<h2>v3.29.1</h2>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>3.29.1 - 27 Jun 2025</h2>
<ul>
<li>Fix bug in PR analysis where user-provided <code>include</code>
query filter fails to exclude non-included queries. <a
href="https://redirect.github.com/github/codeql-action/pull/2938">#2938</a></li>
<li>Update default CodeQL bundle version to 2.22.1. <a
href="https://redirect.github.com/github/codeql-action/pull/2950">#2950</a></li>
</ul>
<p>See the full <a
href="https://github.com/github/codeql-action/blob/v3.29.1/CHANGELOG.md">CHANGELOG.md</a>
for more information.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/blob/main/CHANGELOG.md">github/codeql-action's
changelog</a>.</em></p>
<blockquote>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>[UNRELEASED]</h2>
<p>No user facing changes.</p>
<h2>3.29.2 - 30 Jun 2025</h2>
<ul>
<li>Experimental: When the <code>quality-queries</code> input for the
<code>init</code> action is provided with an argument, separate
<code>.quality.sarif</code> files are produced and uploaded for each
language with the results of the specified queries. Do not use this in
production as it is part of an internal experiment and subject to change
at any time. <a
href="https://redirect.github.com/github/codeql-action/pull/2935">#2935</a></li>
</ul>
<h2>3.29.1 - 27 Jun 2025</h2>
<ul>
<li>Fix bug in PR analysis where user-provided <code>include</code>
query filter fails to exclude non-included queries. <a
href="https://redirect.github.com/github/codeql-action/pull/2938">#2938</a></li>
<li>Update default CodeQL bundle version to 2.22.1. <a
href="https://redirect.github.com/github/codeql-action/pull/2950">#2950</a></li>
</ul>
<h2>3.29.0 - 11 Jun 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.22.0. <a
href="https://redirect.github.com/github/codeql-action/pull/2925">#2925</a></li>
<li>Bump minimum CodeQL bundle version to 2.16.6. <a
href="https://redirect.github.com/github/codeql-action/pull/2912">#2912</a></li>
</ul>
<h2>3.28.19 - 03 Jun 2025</h2>
<ul>
<li>The CodeQL Action no longer includes its own copy of the extractor
for the <code>actions</code> language, which is currently in public
preview.
The <code>actions</code> extractor has been included in the CodeQL CLI
since v2.20.6. If your workflow has enabled the <code>actions</code>
language <em>and</em> you have pinned
your <code>tools:</code> property to a specific version of the CodeQL
CLI earlier than v2.20.6, you will need to update to at least CodeQL
v2.20.6 or disable
<code>actions</code> analysis.</li>
<li>Update default CodeQL bundle version to 2.21.4. <a
href="https://redirect.github.com/github/codeql-action/pull/2910">#2910</a></li>
</ul>
<h2>3.28.18 - 16 May 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.3. <a
href="https://redirect.github.com/github/codeql-action/pull/2893">#2893</a></li>
<li>Skip validating SARIF produced by CodeQL for improved performance.
<a
href="https://redirect.github.com/github/codeql-action/pull/2894">#2894</a></li>
<li>The number of threads and amount of RAM used by CodeQL can now be
set via the <code>CODEQL_THREADS</code> and <code>CODEQL_RAM</code>
runner environment variables. If set, these environment variables
override the <code>threads</code> and <code>ram</code> inputs
respectively. <a
href="https://redirect.github.com/github/codeql-action/pull/2891">#2891</a></li>
</ul>
<h2>3.28.17 - 02 May 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.2. <a
href="https://redirect.github.com/github/codeql-action/pull/2872">#2872</a></li>
</ul>
<h2>3.28.16 - 23 Apr 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.1. <a
href="https://redirect.github.com/github/codeql-action/pull/2863">#2863</a></li>
</ul>
<h2>3.28.15 - 07 Apr 2025</h2>
<ul>
<li>Fix bug where the action would fail if it tried to produce a debug
artifact with more than 65535 files. <a
href="https://redirect.github.com/github/codeql-action/pull/2842">#2842</a></li>
</ul>
<h2>3.28.14 - 07 Apr 2025</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/github/codeql-action/commit/181d5eefc20863364f96762470ba6f862bdef56b"><code>181d5ee</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2957">#2957</a>
from github/update-v3.29.2-4c57370d0</li>
<li><a
href="https://github.com/github/codeql-action/commit/c77386a9db782647c8e2575da69a3c950786eaca"><code>c77386a</code></a>
Fix changelog PR number</li>
<li><a
href="https://github.com/github/codeql-action/commit/8d43d4ecec27cc4205b0eaaf2e9b4bf9ee9a305b"><code>8d43d4e</code></a>
Update changelog for v3.29.2</li>
<li><a
href="https://github.com/github/codeql-action/commit/4c57370d0304fbff638216539f81d9163f77712a"><code>4c57370</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2935">#2935</a>
from github/mbg/interpret-cq-results</li>
<li><a
href="https://github.com/github/codeql-action/commit/2830b750e5012e0a57cb63888cd5720f2326ca5c"><code>2830b75</code></a>
Add changelog entry</li>
<li><a
href="https://github.com/github/codeql-action/commit/aa72ddaeada556e7d763c9a0afb01f2c2a365e1c"><code>aa72dda</code></a>
Merge branch 'main' into mbg/interpret-cq-results</li>
<li><a
href="https://github.com/github/codeql-action/commit/65d1e45f0ba420207efc0f1f6d90c63dcbc97551"><code>65d1e45</code></a>
Rename <code>SARIF_UPLOAD_ENDPOINT</code> members</li>
<li><a
href="https://github.com/github/codeql-action/commit/362ebf85dad6ee3df420db2cec285490b289a61f"><code>362ebf8</code></a>
Check both SARIF files in <code>quality-queries.yml</code> test</li>
<li><a
href="https://github.com/github/codeql-action/commit/10a3e4b17dd8a1cee767213c309bd4b1e8251eab"><code>10a3e4b</code></a>
Fix formatting</li>
<li><a
href="https://github.com/github/codeql-action/commit/8593ea65e2bf97ec2caa80fb0e464ed8c42c0fae"><code>8593ea6</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2954">#2954</a>
from github/mergeback/v3.29.1-to-main-39edc492</li>
<li>Additional commits viewable in <a
href="https://github.com/github/codeql-action/compare/ce28f5bb42b7a9f2c824e633a3f6ee835bab6858...181d5eefc20863364f96762470ba6f862bdef56b">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.29.0&new-version=3.29.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 15:39:07 +01:00
Anthony Stirling df88b01b5d restore OCRMyPDF and ghostscript compression (#3846)
# Description of Changes

This pull request introduces enhancements to tool dependencies, endpoint
configurations, and Dockerfile packages. The changes primarily focus on
adding support for new tools (Ghostscript and OCRmyPDF), improving
endpoint management, and refining dependency checks. Below is a summary
of the most important changes grouped by theme:

### Tool Integration and Dependency Management:
* Added support for Ghostscript and OCRmyPDF in the session limits and
timeout configurations (`ApplicationProperties.java`).
[[1]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R548-R549)
[[2]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R582-R589)
[[3]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R602-R603)
[[4]](diffhunk://#diff-0642cd5c54d57a80f1f9c26cb26677f58778dc889cb59ebfc07b7512e74a8886R636-R643)
* Updated `Processes` enum and `ProcessExecutor` logic to include
Ghostscript and OCRmyPDF, enabling their session limits and timeout
handling.
[[1]](diffhunk://#diff-b0afb37bdac8b0f1a10aca87b0244b133ac9b05518088b6f4c6c2bf48f859fdaR87-R96)
[[2]](diffhunk://#diff-b0afb37bdac8b0f1a10aca87b0244b133ac9b05518088b6f4c6c2bf48f859fdaR141-R150)
[[3]](diffhunk://#diff-b0afb37bdac8b0f1a10aca87b0244b133ac9b05518088b6f4c6c2bf48f859fdaL281-R303)
* Integrated Ghostscript and OCRmyPDF into the external dependency check
mechanism (`ExternalAppDepConfig.java`).
[[1]](diffhunk://#diff-511713d04e0545670bfb44e70e84235288e91db0ef219e423628746f28e67cfcR37-R38)
[[2]](diffhunk://#diff-511713d04e0545670bfb44e70e84235288e91db0ef219e423628746f28e67cfcR114-R115)

### Endpoint Configuration Enhancements:
* Improved endpoint management by introducing functional group
overrides, tool group fallbacks, and alternative tool group handling
(`EndpointConfiguration.java`).
[[1]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dR24-R25)
[[2]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dR39-R178)
[[3]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dL264-R357)
[[4]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dR399-R437)
* Added new endpoint groups and alternatives for multi-tool endpoints
like "repair," "compress-pdf," and "ocr-pdf," supporting both
Ghostscript and OCRmyPDF.
[[1]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dL215-L216)
[[2]](diffhunk://#diff-d6f76bfe88b1a7f347c10cc6956f8e7874b66772c6ac0ac432bedd3c7e8c372dL264-R357)

### Dockerfile Updates:
* Removed `qpdf` and added `unpaper` and `ocrmypdf` to the package list
in `Dockerfile` and `Dockerfile.fat` to enable advanced OCR and PDF
manipulation features.
[[1]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557L54)
[[2]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R71-R75)
[[3]](diffhunk://#diff-571631582b988e88c52c86960cc083b0b8fa63cf88f056f26e9e684195221c27L79-R89)

### Code Cleanup and Refactoring:
* Refactored `TempFileRegistry` to improve readability by simplifying
variable initialization.
[[1]](diffhunk://#diff-ed08845255ade38ea6e28a959ed967957611025fb7f8f5dcf4543b5f200a5fecL12)
[[2]](diffhunk://#diff-ed08845255ade38ea6e28a959ed967957611025fb7f8f5dcf4543b5f200a5fecL30-R29)
* Updated `CompressController` to dynamically check tool group
availability using the new endpoint configuration logic.
[[1]](diffhunk://#diff-fc8dc1845d34077a089d9265521ce8c8d6104a8b79137e1de5310e30ffb0348aR50)
[[2]](diffhunk://#diff-fc8dc1845d34077a089d9265521ce8c8d6104a8b79137e1de5310e30ffb0348aR65-R76)

These changes collectively enhance the application's capabilities for
PDF processing, improve dependency management, and streamline endpoint
handling.

---

## 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-07-01 14:50:56 +01:00
Ludy ee0426fa18 refactor: replace deprecated fields() with propertyStream() in ApiDocService (#3838)
# Description of Changes

- Replaced usage of the deprecated `fields()` method on `JsonNode` with
`propertyStream()` in `ApiDocService`.
- This change ensures compatibility with future Jackson versions and
avoids deprecation warnings.

---

## Checklist

### General

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

### Documentation

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

### UI Changes (if applicable)

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

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-06-30 22:32:30 +01:00
dmiljkandroot 2ad25d43a0 Updated Serbian translation and ignore list (#3844)
This PR completes the Serbian translation file
(`messages_sr_LATN_RS.properties`) to 100%, as of this moment.

Also updated `ignore_translation.toml` to reflect which lines should be
skipped during validation.

Please deploy the test instance as mentioned. Thanks!


---

## 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.

---------

Co-authored-by: root <root@ubuntu-22.bxy.rs>
2025-06-30 22:31:53 +01:00
307 changed files with 8840 additions and 143779 deletions
+1 -1
View File
@@ -115,7 +115,7 @@ labels:
- '.editorconfig'
- '.pre-commit-config'
- '.github/workflows/pre_commit.yml'
- 'HowToAddNewLanguage.md'
- 'devGuide/.*'
- label: 'Test'
files:
+6 -4
View File
@@ -1,5 +1,6 @@
# Description of Changes
<!--
Please provide a summary of the changes, including:
- What was changed
@@ -7,6 +8,7 @@ Please provide a summary of the changes, including:
- Any challenges encountered
Closes #(issue_number)
-->
---
@@ -15,15 +17,15 @@ Closes #(issue_number)
### 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 read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/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/devGuide/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)
- [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
### UI Changes (if applicable)
@@ -31,4 +33,4 @@ Closes #(issue_number)
### 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.
- [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
+16 -16
View File
@@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_pre_commit.txt' '.github\scripts\requirements_pre_commit.in'
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_pre_commit.txt' --strip-extras '.github\scripts\requirements_pre_commit.in'
#
cfgv==3.4.0 \
--hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \
@@ -12,25 +12,25 @@ distlib==0.3.9 \
--hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \
--hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403
# via virtualenv
filelock==3.16.1 \
--hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \
--hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435
filelock==3.18.0 \
--hash=sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2 \
--hash=sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de
# via virtualenv
identify==2.6.5 \
--hash=sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566 \
--hash=sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc
identify==2.6.12 \
--hash=sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2 \
--hash=sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6
# via pre-commit
nodeenv==1.9.1 \
--hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \
--hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9
# via pre-commit
platformdirs==4.3.6 \
--hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \
--hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb
platformdirs==4.3.8 \
--hash=sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc \
--hash=sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4
# via virtualenv
pre-commit==4.0.1 \
--hash=sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2 \
--hash=sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878
pre-commit==4.2.0 \
--hash=sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146 \
--hash=sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd
# via -r .github\scripts\requirements_pre_commit.in
pyyaml==6.0.2 \
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
@@ -87,7 +87,7 @@ pyyaml==6.0.2 \
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
# via pre-commit
virtualenv==20.28.1 \
--hash=sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb \
--hash=sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329
virtualenv==20.31.2 \
--hash=sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11 \
--hash=sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af
# via pre-commit
+4 -4
View File
@@ -2,9 +2,9 @@
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_sync_readme.txt' '.github\scripts\requirements_sync_readme.in'
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_sync_readme.txt' --strip-extras '.github\scripts\requirements_sync_readme.in'
#
tomlkit==0.13.2 \
--hash=sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde \
--hash=sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79
tomlkit==0.13.3 \
--hash=sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1 \
--hash=sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0
# via -r .github\scripts\requirements_sync_readme.in
-310
View File
@@ -1,310 +0,0 @@
name: Auto PR V2 Deployment
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
check-pr:
runs-on: ubuntu-latest
outputs:
should_deploy: ${{ steps.check-conditions.outputs.should_deploy }}
pr_number: ${{ github.event.number }}
pr_repository: ${{ steps.get-pr-info.outputs.repository }}
pr_ref: ${{ steps.get-pr-info.outputs.ref }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
with:
egress-policy: audit
- name: Check deployment conditions
id: check-conditions
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
run: |
echo "PR Title: $PR_TITLE"
echo "PR Author: $PR_AUTHOR"
echo "PR Branch: $PR_BRANCH"
# Check if author is authorized
authorized_users=(
"Frooodle"
"sf298"
"Ludy87"
"LaserKaspar"
"sbplat"
"reecebrowne"
"DarioGii"
"ConnorYoh"
)
is_authorized=false
for user in "${authorized_users[@]}"; do
if [[ "$PR_AUTHOR" == "$user" ]]; then
is_authorized=true
break
fi
done
# Check if title contains V2/version2 keywords (case insensitive)
has_v2_keyword=false
if [[ "$PR_TITLE" =~ [Vv]2 ]] || [[ "$PR_TITLE" =~ [Vv]ersion.?2 ]] || [[ "$PR_TITLE" =~ [Vv]ersion.?[Tt]wo ]]; then
has_v2_keyword=true
fi
# Check if branch name contains V2 or react keywords (case insensitive)
has_branch_keyword=false
if [[ "$PR_BRANCH" =~ [Vv]2 ]] || [[ "$PR_BRANCH" =~ [Rr]eact ]]; then
has_branch_keyword=true
fi
if [[ "$is_authorized" == "true" ]] && [[ "$has_v2_keyword" == "true" || "$has_branch_keyword" == "true" ]]; then
echo "✅ Deployment conditions met"
echo "should_deploy=true" >> $GITHUB_OUTPUT
else
echo "❌ Deployment conditions not met"
echo " - Authorized user: $is_authorized"
echo " - Has V2 keyword in title: $has_v2_keyword"
echo " - Has V2/React keyword in branch: $has_branch_keyword"
echo "should_deploy=false" >> $GITHUB_OUTPUT
fi
- name: Get PR repository and ref
id: get-pr-info
if: steps.check-conditions.outputs.should_deploy == 'true'
run: |
# For forks, use the full repository name, for internal PRs use the current repo
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
repository="${{ github.event.pull_request.head.repo.full_name }}"
else
repository="${{ github.repository }}"
fi
echo "repository=$repository" >> $GITHUB_OUTPUT
echo "ref=${{ github.event.pull_request.head.ref }}" >> $GITHUB_OUTPUT
deploy-v2-pr:
needs: check-pr
runs-on: ubuntu-latest
if: needs.check-pr.outputs.should_deploy == 'true'
# Concurrency control - only one deployment per PR at a time
concurrency:
group: v2-deploy-pr-${{ needs.check-pr.outputs.pr_number }}
cancel-in-progress: true
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Add deployment started comment
id: deployment-started
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const { owner, repo } = context.repo;
const prNumber = ${{ needs.check-pr.outputs.pr_number }};
// Delete previous V2 deployment comments to avoid clutter
const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number: prNumber,
per_page: 100
});
const v2Comments = comments.filter(comment =>
comment.body.includes('🚀 **Auto-deploying V2 version**') ||
comment.body.includes('## 🚀 V2 Auto-Deployment Complete!') ||
comment.body.includes('❌ **V2 Auto-deployment failed**')
);
for (const comment of v2Comments) {
console.log(`Deleting old V2 comment: ${comment.id}`);
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: comment.id
});
}
// Create new deployment started comment
const { data: newComment } = await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: `🚀 **Auto-deploying V2 version** for PR #${prNumber}...\n\n_This is an automated deployment triggered by V2/version2 keywords in the PR title or V2/React keywords in the branch name._\n\n⚠️ **Note:** If new commits are pushed during deployment, this build will be cancelled and replaced with the latest version.`
});
return newComment.id;
- name: Checkout PR
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: ${{ needs.check-pr.outputs.pr_repository }}
ref: ${{ needs.check-pr.outputs.pr_ref }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
java-version: "17"
distribution: "temurin"
- name: Build backend
run: |
export DISABLE_ADDITIONAL_FEATURES=true
./gradlew clean build
env:
STIRLING_PDF_DESKTOP_UI: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Get version number
id: versionNumber
run: |
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
- name: Login to Docker Hub
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push V2 monolith image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ./docker/monolith/Dockerfile
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-pr-${{ needs.check-pr.outputs.pr_number }}
build-args: VERSION_TAG=v2-alpha
platforms: linux/amd64
- name: Set up SSH
run: |
mkdir -p ~/.ssh/
echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
sudo chmod 600 ../private.key
- name: Deploy V2 to VPS
id: deploy
run: |
# Use same port strategy as regular PRs - just the PR number
V2_PORT=${{ needs.check-pr.outputs.pr_number }}
# Create docker-compose for V2 monolith
cat > docker-compose.yml << EOF
version: '3.3'
services:
stirling-pdf-v2:
container_name: stirling-pdf-v2-pr-${{ needs.check-pr.outputs.pr_number }}
image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-pr-${{ needs.check-pr.outputs.pr_number }}
ports:
- "${V2_PORT}:80" # Frontend port (same as regular PRs)
volumes:
- /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/data:/usr/share/tessdata:rw
- /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/config:/configs:rw
- /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false"
SYSTEM_DEFAULTLOCALE: en-GB
UI_APPNAME: "Stirling-PDF V2 PR#${{ needs.check-pr.outputs.pr_number }}"
UI_HOMEDESCRIPTION: "V2 PR#${{ needs.check-pr.outputs.pr_number }} - Frontend/Backend Split Architecture"
UI_APPNAMENAVBAR: "V2 PR#${{ needs.check-pr.outputs.pr_number }}"
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "false"
restart: on-failure:5
EOF
# Deploy to VPS
scp -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null docker-compose.yml ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }}:/tmp/docker-compose-v2.yml
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << ENDSSH
# Create V2 PR-specific directories
mkdir -p /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/{data,config,logs}
# Move docker-compose file to correct location
mv /tmp/docker-compose-v2.yml /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/docker-compose.yml
# Stop any existing container and clean up
cd /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}
docker-compose down --remove-orphans 2>/dev/null || true
# Start the new container
docker-compose pull
docker-compose up -d
# Clean up unused Docker resources to save space
docker system prune -af --volumes
ENDSSH
# Set port for output
echo "v2_port=${V2_PORT}" >> $GITHUB_OUTPUT
- name: Post V2 deployment URL to PR
if: success()
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const { owner, repo } = context.repo;
const prNumber = ${{ needs.check-pr.outputs.pr_number }};
const v2Port = ${{ steps.deploy.outputs.v2_port }};
// Delete the "deploying..." comment since we're posting the final result
const deploymentStartedId = ${{ steps.deployment-started.outputs.result }};
if (deploymentStartedId) {
console.log(`Deleting deployment started comment: ${deploymentStartedId}`);
try {
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: deploymentStartedId
});
} catch (error) {
console.log(`Could not delete deployment started comment: ${error.message}`);
}
}
const deploymentUrl = `http://${{ secrets.VPS_HOST }}:${v2Port}`;
const commentBody = `## 🚀 V2 Auto-Deployment Complete!\n\n` +
`Your V2 PR with the new frontend/backend split architecture has been deployed!\n\n` +
`🔗 **V2 Test URL:** [${deploymentUrl}](${deploymentUrl})\n\n` +
`_This deployment will be automatically cleaned up when the PR is closed._\n\n` +
`🔄 **Auto-deployed** because PR title or branch name contains V2/version2/React keywords.`;
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: commentBody
});
@@ -30,6 +30,7 @@ jobs:
github.event.comment.user.login == 'sbplat' ||
github.event.comment.user.login == 'reecebrowne' ||
github.event.comment.user.login == 'DarioGii' ||
github.event.comment.user.login == 'EthanHealy01' ||
github.event.comment.user.login == 'ConnorYoh'
)
outputs:
@@ -42,7 +43,7 @@ jobs:
enable_enterprise: ${{ steps.check-pro-flag.outputs.enable_enterprise }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -149,7 +150,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+3 -3
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -91,7 +91,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -135,7 +135,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
pull-requests: write # Allow writing to pull requests
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
repository-projects: write # Required for enabling automerge
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
issues: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+19 -15
View File
@@ -21,27 +21,31 @@ jobs:
versionMac: ${{ steps.versionNumberMac.outputs.versionNumberMac }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Get version number
- name: Set up JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
distribution: 'temurin'
java-version: '21'
# ✅ Get version from Gradle
- name: Get version number
id: versionNumber
run: |
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
VERSION=$(./gradlew printVersion --quiet | tail -1)
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
# ✅ Get Mac-specific version from Gradle
- name: Get version number mac
id: versionNumberMac
run: |
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
CURRENT_YEAR=$(date +'%Y')
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
MAC_VERSION="$CURRENT_YEAR.${VERSION_PARTS[1]:-0}.${VERSION_PARTS[2]:-0}"
echo "versionNumberMac=$MAC_VERSION" >> $GITHUB_OUTPUT
VERSION_MAC=$(./gradlew printMacVersion --quiet | tail -1)
echo "versionNumberMac=$VERSION_MAC" >> $GITHUB_OUTPUT
build-portable:
needs: read_versions
@@ -56,7 +60,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -82,7 +86,7 @@ jobs:
run: |
mkdir ./binaries
mv ./build/launch4j/Stirling-PDF.exe ./binaries/win-Stirling-PDF-portable-Server${{ matrix.file_suffix }}.exe
mv ./build/libs/Stirling-PDF-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar
mv ./stirling-pdf/build/libs/stirling-pdf-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar
- name: Upload build artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
@@ -106,7 +110,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -144,7 +148,7 @@ jobs:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -234,7 +238,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -248,7 +252,7 @@ jobs:
- name: Install Cosign
if: matrix.os == 'windows-latest'
uses: sigstore/cosign-installer@fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3 # v3.9.0
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
- name: Generate key pair
if: matrix.os == 'windows-latest'
@@ -297,7 +301,7 @@ jobs:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+5 -5
View File
@@ -18,7 +18,7 @@ jobs:
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -42,7 +42,7 @@ jobs:
- name: Install cosign
if: github.ref == 'refs/heads/master'
uses: sigstore/cosign-installer@fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3 # v3.9.0
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
with:
cosign-release: "v2.4.1"
@@ -77,6 +77,7 @@ jobs:
- name: Generate tags
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
@@ -86,11 +87,11 @@ jobs:
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and push main Dockerfile
id: build-push-regular
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
if: github.ref != 'refs/heads/main'
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
@@ -153,7 +154,6 @@ jobs:
- name: Generate tags fat
id: meta3
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
@@ -163,11 +163,11 @@ jobs:
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and push main Dockerfile fat
id: build-push-fat
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
if: github.ref != 'refs/heads/main'
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
+4 -4
View File
@@ -23,7 +23,7 @@ jobs:
version: ${{ steps.versionNumber.outputs.versionNumber }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -83,7 +83,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -95,7 +95,7 @@ jobs:
run: ls -R
- name: Install Cosign
uses: sigstore/cosign-installer@fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3 # v3.9.0
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
- name: Generate key pair
run: cosign generate-key-pair
@@ -161,7 +161,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+2 -2
View File
@@ -34,7 +34,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -74,6 +74,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
with:
sarif_file: results.sarif
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
+3 -3
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -57,8 +57,8 @@ jobs:
- name: Run git add
run: |
git add README.md
git diff --staged --quiet || git commit -m ":memo: Sync README.md" || echo "No changes detected"
git add README.md scripts/ignore_translation.toml
git diff --staged --quiet || git commit -m ":memo: Sync README.md & scripts/ignore_translation.toml" || echo "No changes detected"
- name: Create Pull Request
if: always()
+3 -3
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -105,7 +105,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
@@ -134,7 +134,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
with:
egress-policy: audit
-3
View File
@@ -27,7 +27,6 @@ clientWebUI/
!cucumber/exampleFiles/
!cucumber/exampleFiles/example_html.zip
exampleYmlFiles/stirling/
stirling/
/testing/file_snapshots
SwaggerDoc.json
@@ -194,8 +193,6 @@ id_ed25519.pub
.pytest_cache
.ipynb_checkpoints
**/jcef-bundle/
# node_modules
-124
View File
@@ -1,124 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Common Development Commands
### Build and Test
- **Build project**: `./gradlew clean build`
- **Run locally**: `./gradlew bootRun`
- **Full test suite**: `./test.sh` (builds all Docker variants and runs comprehensive tests)
- **Code formatting**: `./gradlew spotlessApply` (runs automatically before compilation)
### Docker Development
- **Build ultra-lite**: `docker build -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .`
- **Build standard**: `docker build -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .`
- **Build fat version**: `docker build -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile.fat .`
- **Example compose files**: Located in `exampleYmlFiles/` directory
### Security Mode Development
Set `DOCKER_ENABLE_SECURITY=true` environment variable to enable security features during development. This is required for testing the full version locally.
### Frontend Development
- **Frontend dev server**: `cd frontend && npm run dev` (requires backend on localhost:8080)
- **Tech Stack**: Vite + React + TypeScript + Mantine UI + TailwindCSS
- **Proxy Configuration**: Vite proxies `/api/*` calls to backend (localhost:8080)
- **Build Process**: DO NOT run build scripts manually - builds are handled by CI/CD pipelines
- **Package Installation**: DO NOT run npm install commands - package management handled separately
#### Tailwind CSS Setup (if not already installed)
```bash
cd frontend
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
## Architecture Overview
### Project Structure
- **Backend**: Spring Boot application with Thymeleaf templating
- **Frontend**: React-based SPA in `/frontend` directory (replacing legacy Thymeleaf templates)
- **Current Status**: Active development to replace Thymeleaf UI with modern React SPA
- **File Storage**: IndexedDB for client-side file persistence and thumbnails
- **Internationalization**: JSON-based translations (converted from backend .properties)
- **URL Parameters**: Deep linking support for tool states and configurations
- **PDF Processing**: PDFBox for core PDF operations, LibreOffice for conversions, PDF.js for client-side rendering
- **Security**: Spring Security with optional authentication (controlled by `DOCKER_ENABLE_SECURITY`)
- **Configuration**: YAML-based configuration with environment variable overrides
### Controller Architecture
- **API Controllers** (`src/main/java/.../controller/api/`): REST endpoints for PDF operations
- Organized by function: converters, security, misc, pipeline
- Follow pattern: `@RestController` + `@RequestMapping("/api/v1/...")`
- **Web Controllers** (`src/main/java/.../controller/web/`): Serve Thymeleaf templates
- Pattern: `@Controller` + return template names
### Key Components
- **SPDFApplication.java**: Main application class with desktop UI and browser launching logic
- **ConfigInitializer**: Handles runtime configuration and settings files
- **Pipeline System**: Automated PDF processing workflows via `PipelineController`
- **Security Layer**: Authentication, authorization, and user management (when enabled)
### Template System (Legacy + Modern)
- **Legacy Thymeleaf Templates**: Located in `src/main/resources/templates/` (being phased out)
- **Modern React Components**: Located in `frontend/src/components/` and `frontend/src/tools/`
- **Static Assets**: CSS, JS, and resources in `src/main/resources/static/` (legacy) + `frontend/public/` (modern)
- **Internationalization**:
- Backend: `messages_*.properties` files
- Frontend: JSON files in `frontend/public/locales/` (converted from .properties)
- Conversion Script: `scripts/convert_properties_to_json.py`
### Configuration Modes
- **Ultra-lite**: Basic PDF operations only
- **Standard**: Full feature set
- **Fat**: Pre-downloaded dependencies for air-gapped environments
- **Security Mode**: Adds authentication, user management, and enterprise features
### Testing Strategy
- **Integration Tests**: Cucumber tests in `testing/cucumber/`
- **Docker Testing**: `test.sh` validates all Docker variants
- **Manual Testing**: No unit tests currently - relies on UI and API testing
## Development Workflow
1. **Local Development**:
- Backend: `./gradlew bootRun` (runs on localhost:8080)
- Frontend: `cd frontend && npm run dev` (runs on localhost:5173, proxies to backend)
2. **Docker Testing**: Use `./test.sh` before submitting PRs
3. **Code Style**: Spotless enforces Google Java Format automatically
4. **Translations**:
- Backend: Use helper scripts in `/scripts` for multi-language updates
- Frontend: Update JSON files in `frontend/public/locales/` or use conversion script
5. **Documentation**: API docs auto-generated and available at `/swagger-ui/index.html`
## Frontend Migration Notes
- **Current Branch**: `feature/react-overhaul` - Active React SPA development
- **Migration Status**: Core tools (Split, Merge, Compress) converted to React with URL parameter support
- **File Management**: Implemented IndexedDB storage with thumbnail generation using PDF.js
- **Tools Architecture**: Each tool receives `params` and `updateParams` for URL state synchronization
- **Remaining Work**: Convert remaining Thymeleaf templates to React components
## Important Notes
- **Java Version**: Minimum JDK 17, supports and recommends JDK 21
- **Lombok**: Used extensively - ensure IDE plugin is installed
- **Desktop Mode**: Set `STIRLING_PDF_DESKTOP_UI=true` for desktop application mode
- **File Persistence**:
- **Backend**: Designed to be stateless - files are processed in memory/temp locations only
- **Frontend**: Uses IndexedDB for client-side file storage and caching (with thumbnails)
- **Security**: When `DOCKER_ENABLE_SECURITY=false`, security-related classes are excluded from compilation
## Communication Style
- Be direct and to the point
- No apologies or conversational filler
- Answer questions directly without preamble
- Explain reasoning concisely when asked
- Avoid unnecessary elaboration
## Decision Making
- Ask clarifying questions before making assumptions
- Stop and ask when uncertain about project-specific details
- Confirm approach before making structural changes
- Request guidance on preferences (cross-platform vs specific tools, etc.)
- Verify understanding of requirements before proceeding
+13 -2
View File
@@ -25,7 +25,7 @@ Please make sure your Pull Request adheres to the following guidelines:
## Translations
If you would like to add or modify a translation, please see [How to add new languages to Stirling-PDF](HowToAddNewLanguage.md). Also, please create a Pull Request so others can use it!
If you would like to add or modify a translation, please see [How to add new languages to Stirling-PDF](devGuide/HowToAddNewLanguage.md). Also, please create a Pull Request so others can use it!
## Docs
@@ -37,7 +37,18 @@ First, make sure you've read the section [Pull Requests](#pull-requests).
If, at any point in time, you have a question, please feel free to ask in the same issue thread or in our [Discord](https://discord.gg/FJUSXUSYec).
Developers should review our [Developer Guide](DeveloperGuide.md)
## Developer Documentation
For technical guides, setup instructions, and development resources, please see our [Developer Documentation](devGuide/) which includes:
- [Developer Guide](devGuide/DeveloperGuide.md) - Main setup and architecture guide
- [Exception Handling Guide](devGuide/EXCEPTION_HANDLING_GUIDE.md) - Error handling patterns and i18n
- [Translation Guide](devGuide/HowToAddNewLanguage.md) - Adding new languages
- And more in the [devGuide folder](devGuide/)
For configuration and usage guides, see:
- [Database Guide](DATABASE.md) - Database setup and configuration
- [OCR Guide](HowToUseOCR.md) - OCR setup and configuration
## License
+14 -8
View File
@@ -1,4 +1,4 @@
# Backend Dockerfile - Java Spring Boot with all dependencies
# Main stage
FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715
# Copy necessary files
@@ -9,8 +9,8 @@ COPY stirling-pdf/build/libs/*.jar app.jar
ARG VERSION_TAG
LABEL org.opencontainers.image.title="Stirling-PDF Backend"
LABEL org.opencontainers.image.description="Backend service for Stirling-PDF - Java Spring Boot with PDF processing capabilities"
LABEL org.opencontainers.image.title="Stirling-PDF"
LABEL org.opencontainers.image.description="A powerful locally hosted web-based PDF manipulation tool supporting 50+ operations including merging, splitting, conversion, OCR, watermarking, and more."
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.vendor="Stirling-Tools"
@@ -19,7 +19,7 @@ LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
LABEL maintainer="Stirling-Tools"
LABEL org.opencontainers.image.authors="Stirling-Tools"
LABEL org.opencontainers.image.version="${VERSION_TAG}"
LABEL org.opencontainers.image.keywords="PDF, manipulation, backend, API, Spring Boot"
LABEL org.opencontainers.image.keywords="PDF, manipulation, merge, split, convert, OCR, watermark"
# Set Environment Variables
ENV DISABLE_ADDITIONAL_FEATURES=true \
@@ -39,7 +39,8 @@ ENV DISABLE_ADDITIONAL_FEATURES=true \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf
# JDK for app and all dependencies
# JDK for app
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
@@ -50,7 +51,6 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
tini \
bash \
curl \
qpdf \
shadow \
su-exec \
openssl \
@@ -68,12 +68,16 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
tesseract-ocr-data-deu \
tesseract-ocr-data-fra \
tesseract-ocr-data-por \
unpaper \
# CV
py3-opencv \
python3 \
ocrmypdf \
py3-pip \
py3-pillow@testing \
py3-pdf2image@testing && \
py3-pdf2image@testing \
# URW Base 35 fonts for better PDF rendering
font-urw-base35 && \
python3 -m venv /opt/venv && \
/opt/venv/bin/pip install --upgrade pip setuptools && \
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
@@ -82,6 +86,8 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
# Configure URW Base 35 fonts
ln -s /usr/share/fontconfig/conf.avail/69-urw-*.conf /etc/fonts/conf.d/ && \
fc-cache -f -v && \
chmod +x /scripts/* && \
chmod +x /scripts/init.sh && \
@@ -94,4 +100,4 @@ EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
+61
View File
@@ -0,0 +1,61 @@
# dockerfile.dev
# Basisimage: Gradle mit JDK 17 (Debian-basiert)
FROM gradle:8.14-jdk17
# Als Root-Benutzer arbeiten, um benötigte Pakete zu installieren
USER root
# Set GRADLE_HOME und füge Gradle zum PATH hinzu
ENV GRADLE_HOME=/opt/gradle
ENV PATH="$GRADLE_HOME/bin:$PATH"
# Update und Installation zusätzlicher Pakete (Debian/Ubuntu-basiert)
RUN apt-get update && apt-get install -y \
sudo \
libreoffice \
poppler-utils \
qpdf \
# settings.yml | tessdataDir: /usr/share/tesseract-ocr/5/tessdata
tesseract-ocr \
tesseract-ocr-eng \
fonts-terminus fonts-dejavu fonts-font-awesome fonts-noto fonts-noto-core fonts-noto-cjk fonts-noto-extra fonts-liberation fonts-linuxlibertine fonts-urw-base35 \
python3-uno \
python3-venv \
# ss -tln
iproute2 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Setze die Environment Variable für setuptools
ENV SETUPTOOLS_USE_DISTUTILS=local \
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
TMPDIR=/tmp/stirling-pdf \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf
# Installation der benötigten Python-Pakete
RUN python3 -m venv --system-site-packages /opt/venv \
&& . /opt/venv/bin/activate \
&& pip install --upgrade pip setuptools \
&& pip install --no-cache-dir WeasyPrint pdf2image pillow unoserver opencv-python-headless pre-commit
# Füge den venv-Pfad zur globalen PATH-Variable hinzu, damit die Tools verfügbar sind
ENV PATH="/opt/venv/bin:$PATH"
COPY . /workspace
RUN mkdir -p /tmp/stirling-pdf \
&& fc-cache -f -v \
&& adduser --disabled-password --gecos '' devuser \
&& chown -R devuser:devuser /home/devuser /workspace /tmp/stirling-pdf
RUN echo "devuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/devuser \
&& chmod 0440 /etc/sudoers.d/devuser
# Setze das Arbeitsverzeichnis (wird später per Bind-Mount überschrieben)
WORKDIR /workspace
RUN chmod +x /workspace/.devcontainer/git-init.sh
RUN sudo chmod +x /workspace/.devcontainer/init-setup.sh
# Wechsel zum NichtRoot Benutzer
USER devuser
@@ -1,4 +1,3 @@
# Backend fat Dockerfile - Java Spring Boot with all dependencies and build stage
# Build the application
FROM gradle:8.14-jdk21 AS build
@@ -53,6 +52,7 @@ ENV DISABLE_ADDITIONAL_FEATURES=true \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf
# JDK for app
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
@@ -76,16 +76,17 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced featues)
qpdf \
tesseract-ocr-data-eng \
tesseract-ocr-data-chi_sim \
tesseract-ocr-data-deu \
tesseract-ocr-data-fra \
tesseract-ocr-data-por \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra font-liberation font-linux-libertine \
unpaper \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra font-liberation font-linux-libertine font-urw-base35 \
# CV
py3-opencv \
python3 \
ocrmypdf \
py3-pip \
py3-pillow@testing \
py3-pdf2image@testing && \
@@ -97,6 +98,8 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
# Configure URW Base 35 fonts
ln -s /usr/share/fontconfig/conf.avail/69-urw-*.conf /etc/fonts/conf.d/ && \
fc-cache -f -v && \
chmod +x /scripts/* && \
chmod +x /scripts/init.sh && \
@@ -108,4 +111,4 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
@@ -1,4 +1,4 @@
# Backend ultra-lite Dockerfile - Java Spring Boot with minimal dependencies
# use alpine
FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715
ARG VERSION_TAG
+40 -40
View File
@@ -29,7 +29,7 @@ All documentation available at [https://docs.stirlingpdf.com/](https://docs.stir
- API for integration with external scripts
- Optional Login and Authentication support (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/System%20and%20Security) for documentation)
- Database Backup and Import (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/DATABASE) for documentation)
- Enterprise features like SSO see [here](https://docs.stirlingpdf.com/Enterprise%20Edition)
- Enterprise features like SSO (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/Single%20Sign-On%20Configuration) for documentation)
## PDF Features
@@ -116,47 +116,47 @@ Stirling-PDF currently supports 40 languages!
| Language | Progress |
| -------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![65%](https://geps.dev/progress/65) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![65%](https://geps.dev/progress/65) |
| Basque (Euskara) (eu_ES) | ![38%](https://geps.dev/progress/38) |
| Bulgarian (Български) (bg_BG) | ![72%](https://geps.dev/progress/72) |
| Catalan (Català) (ca_CA) | ![71%](https://geps.dev/progress/71) |
| Croatian (Hrvatski) (hr_HR) | ![64%](https://geps.dev/progress/64) |
| Czech (Česky) (cs_CZ) | ![74%](https://geps.dev/progress/74) |
| Danish (Dansk) (da_DK) | ![65%](https://geps.dev/progress/65) |
| Dutch (Nederlands) (nl_NL) | ![63%](https://geps.dev/progress/63) |
| Arabic (العربية) (ar_AR) | ![63%](https://geps.dev/progress/63) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![63%](https://geps.dev/progress/63) |
| Basque (Euskara) (eu_ES) | ![37%](https://geps.dev/progress/37) |
| Bulgarian (Български) (bg_BG) | ![70%](https://geps.dev/progress/70) |
| Catalan (Català) (ca_CA) | ![69%](https://geps.dev/progress/69) |
| Croatian (Hrvatski) (hr_HR) | ![62%](https://geps.dev/progress/62) |
| Czech (Česky) (cs_CZ) | ![71%](https://geps.dev/progress/71) |
| Danish (Dansk) (da_DK) | ![63%](https://geps.dev/progress/63) |
| Dutch (Nederlands) (nl_NL) | ![61%](https://geps.dev/progress/61) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![73%](https://geps.dev/progress/73) |
| German (Deutsch) (de_DE) | ![92%](https://geps.dev/progress/92) |
| Greek (Ελληνικά) (el_GR) | ![71%](https://geps.dev/progress/71) |
| Hindi (हिंदी) (hi_IN) | ![71%](https://geps.dev/progress/71) |
| French (Français) (fr_FR) | ![91%](https://geps.dev/progress/91) |
| German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) |
| Greek (Ελληνικά) (el_GR) | ![69%](https://geps.dev/progress/69) |
| Hindi (हिंदी) (hi_IN) | ![68%](https://geps.dev/progress/68) |
| Hungarian (Magyar) (hu_HU) | ![99%](https://geps.dev/progress/99) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![65%](https://geps.dev/progress/65) |
| Irish (Gaeilge) (ga_IE) | ![72%](https://geps.dev/progress/72) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![63%](https://geps.dev/progress/63) |
| Irish (Gaeilge) (ga_IE) | ![70%](https://geps.dev/progress/70) |
| Italian (Italiano) (it_IT) | ![98%](https://geps.dev/progress/98) |
| Japanese (日本語) (ja_JP) | ![72%](https://geps.dev/progress/72) |
| Korean (한국어) (ko_KR) | ![71%](https://geps.dev/progress/71) |
| Norwegian (Norsk) (no_NB) | ![70%](https://geps.dev/progress/70) |
| Persian (فارسی) (fa_IR) | ![68%](https://geps.dev/progress/68) |
| Polish (Polski) (pl_PL) | ![76%](https://geps.dev/progress/76) |
| Portuguese (Português) (pt_PT) | ![72%](https://geps.dev/progress/72) |
| Portuguese Brazilian (Português) (pt_BR) | ![80%](https://geps.dev/progress/80) |
| Romanian (Română) (ro_RO) | ![61%](https://geps.dev/progress/61) |
| Russian (Русский) (ru_RU) | ![72%](https://geps.dev/progress/72) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![46%](https://geps.dev/progress/46) |
| Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) |
| Slovakian (Slovensky) (sk_SK) | ![54%](https://geps.dev/progress/54) |
| Slovenian (Slovenščina) (sl_SI) | ![75%](https://geps.dev/progress/75) |
| Spanish (Español) (es_ES) | ![78%](https://geps.dev/progress/78) |
| Swedish (Svenska) (sv_SE) | ![69%](https://geps.dev/progress/69) |
| Thai (ไทย) (th_TH) | ![62%](https://geps.dev/progress/62) |
| Tibetan (བོད་ཡིག་) (bo_CN) | ![68%](https://geps.dev/progress/68) |
| Traditional Chinese (繁體中文) (zh_TW) | ![80%](https://geps.dev/progress/80) |
| Turkish (Türkçe) (tr_TR) | ![85%](https://geps.dev/progress/85) |
| Ukrainian (Українська) (uk_UA) | ![75%](https://geps.dev/progress/75) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![60%](https://geps.dev/progress/60) |
| Malayalam (മലയാളം) (ml_IN) | ![77%](https://geps.dev/progress/77) |
| Japanese (日本語) (ja_JP) | ![70%](https://geps.dev/progress/70) |
| Korean (한국어) (ko_KR) | ![69%](https://geps.dev/progress/69) |
| Norwegian (Norsk) (no_NB) | ![67%](https://geps.dev/progress/67) |
| Persian (فارسی) (fa_IR) | ![66%](https://geps.dev/progress/66) |
| Polish (Polski) (pl_PL) | ![73%](https://geps.dev/progress/73) |
| Portuguese (Português) (pt_PT) | ![70%](https://geps.dev/progress/70) |
| Portuguese Brazilian (Português) (pt_BR) | ![77%](https://geps.dev/progress/77) |
| Romanian (Română) (ro_RO) | ![59%](https://geps.dev/progress/59) |
| Russian (Русский) (ru_RU) | ![70%](https://geps.dev/progress/70) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![97%](https://geps.dev/progress/97) |
| Simplified Chinese (简体中文) (zh_CN) | ![90%](https://geps.dev/progress/90) |
| Slovakian (Slovensky) (sk_SK) | ![53%](https://geps.dev/progress/53) |
| Slovenian (Slovenščina) (sl_SI) | ![73%](https://geps.dev/progress/73) |
| Spanish (Español) (es_ES) | ![75%](https://geps.dev/progress/75) |
| Swedish (Svenska) (sv_SE) | ![67%](https://geps.dev/progress/67) |
| Thai (ไทย) (th_TH) | ![60%](https://geps.dev/progress/60) |
| Tibetan (བོད་ཡིག་) (bo_CN) | ![66%](https://geps.dev/progress/66) |
| Traditional Chinese (繁體中文) (zh_TW) | ![77%](https://geps.dev/progress/77) |
| Turkish (Türkçe) (tr_TR) | ![82%](https://geps.dev/progress/82) |
| Ukrainian (Українська) (uk_UA) | ![72%](https://geps.dev/progress/72) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![58%](https://geps.dev/progress/58) |
| Malayalam (മലയാളം) (ml_IN) | ![75%](https://geps.dev/progress/75) |
## Stirling PDF Enterprise
@@ -168,7 +168,7 @@ Check out our [Enterprise docs](https://docs.stirlingpdf.com/Pro)
Join our community:
- [Contribution Guidelines](CONTRIBUTING.md)
- [Translation Guide (How to add custom languages)](HowToAddNewLanguage.md)
- [Translation Guide (How to add custom languages)](devGuide/HowToAddNewLanguage.md)
- [Developer Guide](devGuide/DeveloperGuide.md)
- [Issue Tracker](https://github.com/Stirling-Tools/Stirling-PDF/issues)
- [Discord Community](https://discord.gg/HYmhKj45pU)
- [Developer Guide](DeveloperGuide.md)
+50 -46
View File
@@ -9,12 +9,14 @@ plugins {
id "com.diffplug.spotless" version "7.0.4"
id "com.github.jk1.dependency-license-report" version "2.9"
//id "nebula.lint" version "19.0.3"
id "org.panteleyev.jpackageplugin" version "1.6.1"
id "org.panteleyev.jpackageplugin" version "1.7.3"
id "org.sonarqube" version "6.2.0.5505"
}
import com.github.jk1.license.render.*
import org.gradle.internal.os.OperatingSystem
import org.panteleyev.jpackage.ImageType
import java.nio.file.Files
import java.time.Year
@@ -26,7 +28,7 @@ ext {
bouncycastleVersion = "1.81"
springSecuritySamlVersion = "6.5.1"
openSamlVersion = "4.3.2"
commonmarkVersion = "0.24.0"
commonmarkVersion = "0.25.0"
googleJavaFormatVersion = "1.27.0"
tempJrePath = null
}
@@ -43,39 +45,19 @@ bootJar {
enabled = false
}
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/**'
}
// Configure main class for the root project
springBoot {
mainClass = 'stirling.software.SPDF.SPDFApplication'
}
}
}
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/**'
}
}
}
repositories {
mavenCentral()
maven { url = 'https://build.shibboleth.net/maven/releases' }
}
allprojects {
group = 'stirling.software'
version = '1.0.0'
version = '1.0.2'
configurations.configureEach {
exclude group: 'commons-logging', module: 'commons-logging'
@@ -83,7 +65,6 @@ allprojects {
}
}
tasks.register('writeVersion') {
def propsFile = file("$projectDir/common/src/main/resources/version.properties")
def propsDir = propsFile.parentFile
@@ -108,6 +89,10 @@ tasks.register('writeVersion') {
}
}
tasks.named('createExe') {
dependsOn(":stirling-pdf:bootJar")
}
subprojects {
apply plugin: 'java'
apply plugin: 'java-library'
@@ -120,9 +105,11 @@ subprojects {
sourceCompatibility = JavaVersion.VERSION_17
}
bootJar {
enabled = false
}
if (project.name != "stirling-pdf") {
bootJar {
enabled = false
}
}
repositories {
mavenCentral()
@@ -170,7 +157,7 @@ subprojects {
test {
useJUnitPlatform()
}
tasks.named("processResources") {
dependsOn(rootProject.tasks.writeVersion)
}
@@ -224,6 +211,14 @@ openApi {
waitTimeInSeconds = 60 // Increase the wait time to 60 seconds
}
// Configure the forked spring boot run task to properly delegate to the stirling-pdf module
tasks.named('forkedSpringBootRun') {
dependsOn ':stirling-pdf:bootRun'
doFirst {
println "Delegating forkedSpringBootRun to :stirling-pdf:bootRun"
}
}
//0.11.5 to 2024.11.5
static def getMacVersion(String version) {
def currentYear = Year.now().getValue()
@@ -232,14 +227,15 @@ static def getMacVersion(String version) {
}
jpackage {
input = "build/libs"
destination = "${projectDir}/build/jpackage"
dependsOn(":stirling-pdf:bootJar")
input = layout.projectDirectory.dir("stirling-pdf/build/libs")
destination = layout.projectDirectory.dir("build/jpackage")
mainJar = "Stirling-PDF-${project.version}.jar"
appName = "Stirling PDF"
appVersion = project.version
vendor = "Stirling PDF Inc"
appDescription = "Stirling PDF - Your Local PDF Editor"
icon = "stirling-pdf/src/main/resources/static/favicon.ico"
icon = layout.projectDirectory.file("stirling-pdf/src/main/resources/static/favicon.ico")
verbose = true
// mainClass = "org.springframework.boot.loader.launch.JarLauncher"
@@ -274,15 +270,15 @@ jpackage {
winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates
winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF"
winUpdateUrl = "https://github.com/Stirling-Tools/Stirling-PDF/releases"
type = "exe"
type = ImageType.EXE
installDir = "C:/Program Files/Stirling-PDF"
}
// MacOS-specific configuration
mac {
appVersion = getMacVersion(project.version.toString())
icon = "stirling-pdf/src/main/resources/static/favicon.icns"
type = "dmg"
icon = layout.projectDirectory.file("stirling-pdf/src/main/resources/static/favicon.icns")
type = ImageType.DMG
macPackageIdentifier = "Stirling PDF"
macPackageName = "Stirling PDF"
macAppCategory = "public.app-category.productivity"
@@ -303,8 +299,8 @@ jpackage {
// Linux-specific configuration
linux {
appVersion = project.version
icon = "stirling-pdf/src/main/resources/static/favicon.png"
type = "deb" // Can also use "rpm" for Red Hat-based systems
icon = layout.projectDirectory.file("stirling-pdf/src/main/resources/static/favicon.png")
type = ImageType.DEB // Can also use "rpm" for Red Hat-based systems
// Debian package configuration
//linuxPackageName = "stirlingpdf"
@@ -340,7 +336,7 @@ jpackage {
// Add copyright and license information
copyright = "Copyright © 2025 Stirling PDF Inc."
licenseFile = "LICENSE"
licenseFile = layout.projectDirectory.file("LICENSE")
}
//tasks.wrapper {
@@ -375,7 +371,7 @@ tasks.register('jpackageMacX64') {
commandLine 'jpackage',
'--type', 'dmg',
'--name', 'Stirling PDF (x86_64)',
'--input', 'build/libs',
'--input', 'stirling-pdf/build/libs',
'--main-jar', "Stirling-PDF-${project.version}.jar",
'--main-class', 'org.springframework.boot.loader.launch.JarLauncher',
'--runtime-image', file(jrePath + "/zulu-17.jre/Contents/Home"),
@@ -475,7 +471,7 @@ launch4j {
} else {
headerType = "console"
}
jarTask = tasks.bootJar
jarTask = project(":stirling-pdf").tasks.bootJar
errTitle="Encountered error, do you have Java 21?"
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
@@ -537,6 +533,14 @@ swaggerhubUpload {
}
dependencies {
implementation project(':stirling-pdf')
implementation project(':common')
if (System.getenv('DISABLE_ADDITIONAL_FEATURES') != 'true'
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') != 'true')) {
implementation project(':proprietary')
}
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.2'
}
@@ -10,7 +10,6 @@ import java.util.Properties;
import java.util.function.Predicate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -21,6 +20,7 @@ import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
import org.thymeleaf.spring6.SpringTemplateEngine;
import lombok.Getter;
@@ -148,23 +148,10 @@ public class AppConfig {
}
@Bean(name = "activeSecurity")
public boolean activeSecurity() {
String disableAdditionalFeatures = env.getProperty("DISABLE_ADDITIONAL_FEATURES");
if (disableAdditionalFeatures != null) {
// DISABLE_ADDITIONAL_FEATURES=true means security OFF, so return false
// DISABLE_ADDITIONAL_FEATURES=false means security ON, so return true
return !Boolean.parseBoolean(disableAdditionalFeatures);
}
return env.getProperty("DOCKER_ENABLE_SECURITY", Boolean.class, true);
}
@Bean(name = "missingActiveSecurity")
@ConditionalOnMissingClass(
"stirling.software.proprietary.security.configuration.SecurityConfiguration")
public boolean missingActiveSecurity() {
return true;
return ClassUtils.isPresent(
"stirling.software.proprietary.security.configuration.SecurityConfiguration",
this.getClass().getClassLoader());
}
@Bean(name = "directoryFilter")
@@ -545,6 +545,8 @@ public class ApplicationProperties {
private int calibreSessionLimit;
private int qpdfSessionLimit;
private int tesseractSessionLimit;
private int ghostscriptSessionLimit;
private int ocrMyPdfSessionLimit;
public int getQpdfSessionLimit() {
return qpdfSessionLimit > 0 ? qpdfSessionLimit : 2;
@@ -577,6 +579,14 @@ public class ApplicationProperties {
public int getCalibreSessionLimit() {
return calibreSessionLimit > 0 ? calibreSessionLimit : 1;
}
public int getGhostscriptSessionLimit() {
return ghostscriptSessionLimit > 0 ? ghostscriptSessionLimit : 8;
}
public int getOcrMyPdfSessionLimit() {
return ocrMyPdfSessionLimit > 0 ? ocrMyPdfSessionLimit : 2;
}
}
@Data
@@ -589,6 +599,8 @@ public class ApplicationProperties {
private long calibreTimeoutMinutes;
private long tesseractTimeoutMinutes;
private long qpdfTimeoutMinutes;
private long ghostscriptTimeoutMinutes;
private long ocrMyPdfTimeoutMinutes;
public long getTesseractTimeoutMinutes() {
return tesseractTimeoutMinutes > 0 ? tesseractTimeoutMinutes : 30;
@@ -621,6 +633,14 @@ public class ApplicationProperties {
public long getCalibreTimeoutMinutes() {
return calibreTimeoutMinutes > 0 ? calibreTimeoutMinutes : 30;
}
public long getGhostscriptTimeoutMinutes() {
return ghostscriptTimeoutMinutes > 0 ? ghostscriptTimeoutMinutes : 30;
}
public long getOcrMyPdfTimeoutMinutes() {
return ocrMyPdfTimeoutMinutes > 0 ? ocrMyPdfTimeoutMinutes : 30;
}
}
}
}
@@ -1,6 +1,7 @@
package stirling.software.common.model.job;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -26,14 +27,8 @@ public class JobResult {
/** Error message if the job failed */
private String error;
/** The file ID of the result file, if applicable */
private String fileId;
/** Original file name, if applicable */
private String originalFileName;
/** MIME type of the result, if applicable */
private String contentType;
/** List of result files for jobs that produce files */
private List<ResultFile> resultFiles;
/** Time when the job was created */
private LocalDateTime createdAt;
@@ -64,21 +59,6 @@ public class JobResult {
.build();
}
/**
* Mark this job as complete with a file result
*
* @param fileId The file ID of the result
* @param originalFileName The original file name
* @param contentType The content type of the file
*/
public void completeWithFile(String fileId, String originalFileName, String contentType) {
this.complete = true;
this.fileId = fileId;
this.originalFileName = originalFileName;
this.contentType = contentType;
this.completedAt = LocalDateTime.now();
}
/**
* Mark this job as complete with a general result
*
@@ -101,6 +81,67 @@ public class JobResult {
this.completedAt = LocalDateTime.now();
}
/**
* Mark this job as complete with multiple file results
*
* @param resultFiles The list of result files
*/
public void completeWithFiles(List<ResultFile> resultFiles) {
this.complete = true;
this.resultFiles = new ArrayList<>(resultFiles);
this.completedAt = LocalDateTime.now();
}
/**
* Mark this job as complete with a single file result (convenience method)
*
* @param fileId The file ID of the result
* @param fileName The file name
* @param contentType The content type of the file
* @param fileSize The size of the file in bytes
*/
public void completeWithSingleFile(
String fileId, String fileName, String contentType, long fileSize) {
ResultFile resultFile =
ResultFile.builder()
.fileId(fileId)
.fileName(fileName)
.contentType(contentType)
.fileSize(fileSize)
.build();
completeWithFiles(List.of(resultFile));
}
/**
* Check if this job has file results
*
* @return true if this job has file results, false otherwise
*/
public boolean hasFiles() {
return resultFiles != null && !resultFiles.isEmpty();
}
/**
* Check if this job has multiple file results
*
* @return true if this job has multiple file results, false otherwise
*/
public boolean hasMultipleFiles() {
return resultFiles != null && resultFiles.size() > 1;
}
/**
* Get all result files
*
* @return List of result files
*/
public List<ResultFile> getAllResultFiles() {
if (resultFiles != null && !resultFiles.isEmpty()) {
return Collections.unmodifiableList(resultFiles);
}
return Collections.emptyList();
}
/**
* Add a note to this job
*
@@ -0,0 +1,26 @@
package stirling.software.common.model.job;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/** Represents a single file result from a job execution */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResultFile {
/** The file ID for accessing the file */
private String fileId;
/** The original file name */
private String fileName;
/** MIME type of the file */
private String contentType;
/** Size of the file in bytes */
private long fileSize;
}
@@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.api.PDFFile;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.TempFileRegistry;
@@ -82,7 +83,7 @@ public class CustomPDFDocumentFactory {
*/
public PDDocument load(File file, boolean readOnly) throws IOException {
if (file == null) {
throw new IllegalArgumentException("File cannot be null");
throw ExceptionUtils.createNullArgumentException("File");
}
long fileSize = file.length();
@@ -109,7 +110,7 @@ public class CustomPDFDocumentFactory {
*/
public PDDocument load(Path path, boolean readOnly) throws IOException {
if (path == null) {
throw new IllegalArgumentException("File cannot be null");
throw ExceptionUtils.createNullArgumentException("File");
}
long fileSize = Files.size(path);
@@ -130,7 +131,7 @@ public class CustomPDFDocumentFactory {
/** Load a PDF from byte array with automatic optimization and read-only option. */
public PDDocument load(byte[] input, boolean readOnly) throws IOException {
if (input == null) {
throw new IllegalArgumentException("Input bytes cannot be null");
throw ExceptionUtils.createNullArgumentException("Input bytes");
}
long dataSize = input.length;
@@ -151,7 +152,7 @@ public class CustomPDFDocumentFactory {
/** Load a PDF from InputStream with automatic optimization and read-only option. */
public PDDocument load(InputStream input, boolean readOnly) throws IOException {
if (input == null) {
throw new IllegalArgumentException("InputStream cannot be null");
throw ExceptionUtils.createNullArgumentException("InputStream");
}
// Since we don't know the size upfront, buffer to a temp file
@@ -174,7 +175,7 @@ public class CustomPDFDocumentFactory {
public PDDocument load(InputStream input, String password, boolean readOnly)
throws IOException {
if (input == null) {
throw new IllegalArgumentException("InputStream cannot be null");
throw ExceptionUtils.createNullArgumentException("InputStream");
}
// Since we don't know the size upfront, buffer to a temp file
@@ -292,9 +293,32 @@ public class CustomPDFDocumentFactory {
} else {
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
}
configureResourceCacheIfNeeded(document, contentSize);
return document;
}
/**
* Configure resource cache based on content size and memory constraints. Disables resource
* cache for large files or when memory is low to prevent OOM errors.
*/
private void configureResourceCacheIfNeeded(PDDocument document, long contentSize) {
if (contentSize > LARGE_FILE_THRESHOLD) {
document.setResourceCache(null);
} else {
// Check current memory status for smaller files
long maxMemory = Runtime.getRuntime().maxMemory();
long usedMemory =
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
double freeMemoryPercent = (double) (maxMemory - usedMemory) / maxMemory * 100;
if (freeMemoryPercent < MIN_FREE_MEMORY_PERCENTAGE) {
document.setResourceCache(null);
}
}
}
/** Load a PDF with password protection using adaptive loading strategies */
private PDDocument loadAdaptivelyWithPassword(Object source, long contentSize, String password)
throws IOException {
@@ -313,6 +337,9 @@ public class CustomPDFDocumentFactory {
} else {
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
}
configureResourceCacheIfNeeded(document, contentSize);
return document;
}
@@ -354,7 +381,12 @@ public class CustomPDFDocumentFactory {
private PDDocument loadFromFile(File file, long size, StreamCacheCreateFunction cache)
throws IOException {
return Loader.loadPDF(new DeletingRandomAccessFile(file), "", null, null, cache);
try {
return Loader.loadPDF(new DeletingRandomAccessFile(file), "", null, null, cache);
} catch (IOException e) {
ExceptionUtils.logException("PDF loading from file", e);
throw ExceptionUtils.handlePdfException(e);
}
}
private PDDocument loadFromBytes(byte[] bytes, long size, StreamCacheCreateFunction cache)
@@ -366,7 +398,13 @@ public class CustomPDFDocumentFactory {
Files.write(tempFile, bytes);
return loadFromFile(tempFile.toFile(), size, cache);
}
return Loader.loadPDF(bytes, "", null, null, cache);
try {
return Loader.loadPDF(bytes, "", null, null, cache);
} catch (IOException e) {
ExceptionUtils.logException("PDF loading from bytes", e);
throw ExceptionUtils.handlePdfException(e);
}
}
public PDDocument createNewDocument(MemoryUsageSetting settings) throws IOException {
@@ -399,7 +437,7 @@ public class CustomPDFDocumentFactory {
try {
document.setAllSecurityToBeRemoved(true);
} catch (Exception e) {
log.error("Decryption failed", e);
ExceptionUtils.logException("PDF decryption", e);
throw new IOException("PDF decryption failed", e);
}
}
@@ -131,14 +131,46 @@ public class FileStorage {
return Files.exists(filePath);
}
/**
* Get the size of a file by its ID without loading the content into memory
*
* @param fileId The ID of the file
* @return The size of the file in bytes
* @throws IOException If the file doesn't exist or can't be read
*/
public long getFileSize(String fileId) throws IOException {
Path filePath = getFilePath(fileId);
if (!Files.exists(filePath)) {
throw new IOException("File not found with ID: " + fileId);
}
return Files.size(filePath);
}
/**
* Get the path for a file ID
*
* @param fileId The ID of the file
* @return The path to the file
* @throws IllegalArgumentException if fileId contains path traversal characters or resolves
* outside base directory
*/
private Path getFilePath(String fileId) {
return Path.of(tempDirPath).resolve(fileId);
// Validate fileId to prevent path traversal
if (fileId.contains("..") || fileId.contains("/") || fileId.contains("\\")) {
throw new IllegalArgumentException("Invalid file ID");
}
Path basePath = Path.of(tempDirPath).normalize().toAbsolutePath();
Path resolvedPath = basePath.resolve(fileId).normalize();
// Ensure resolved path is within the base directory
if (!resolvedPath.startsWith(basePath)) {
throw new IllegalArgumentException("File ID resolves to an invalid path");
}
return resolvedPath;
}
/**
@@ -1,15 +1,25 @@
package stirling.software.common.service;
import io.github.pixee.security.ZipSecurity;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.PreDestroy;
@@ -17,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.job.JobResult;
import stirling.software.common.model.job.JobStats;
import stirling.software.common.model.job.ResultFile;
/** Manages async tasks and their results */
@Service
@@ -80,8 +91,53 @@ public class TaskManager {
public void setFileResult(
String jobId, String fileId, String originalFileName, String contentType) {
JobResult jobResult = getOrCreateJobResult(jobId);
jobResult.completeWithFile(fileId, originalFileName, contentType);
log.debug("Set file result for job ID: {} with file ID: {}", jobId, fileId);
// Check if this is a ZIP file that should be extracted
if (isZipFile(contentType, originalFileName)) {
try {
List<ResultFile> extractedFiles =
extractZipToIndividualFiles(fileId, originalFileName);
if (!extractedFiles.isEmpty()) {
jobResult.completeWithFiles(extractedFiles);
log.debug(
"Set multiple file results for job ID: {} with {} files extracted from ZIP",
jobId,
extractedFiles.size());
return;
}
} catch (Exception e) {
log.warn(
"Failed to extract ZIP file for job {}: {}. Falling back to single file result.",
jobId,
e.getMessage());
}
}
// Handle as single file using new ResultFile approach
try {
long fileSize = fileStorage.getFileSize(fileId);
jobResult.completeWithSingleFile(fileId, originalFileName, contentType, fileSize);
log.debug("Set single file result for job ID: {} with file ID: {}", jobId, fileId);
} catch (Exception e) {
log.warn(
"Failed to get file size for job {}: {}. Using size 0.", jobId, e.getMessage());
jobResult.completeWithSingleFile(fileId, originalFileName, contentType, 0);
}
}
/**
* Set the result of a task as multiple files
*
* @param jobId The job ID
* @param resultFiles The list of result files
*/
public void setMultipleFileResults(String jobId, List<ResultFile> resultFiles) {
JobResult jobResult = getOrCreateJobResult(jobId);
jobResult.completeWithFiles(resultFiles);
log.debug(
"Set multiple file results for job ID: {} with {} files",
jobId,
resultFiles.size());
}
/**
@@ -104,7 +160,7 @@ public class TaskManager {
public void setComplete(String jobId) {
JobResult jobResult = getOrCreateJobResult(jobId);
if (jobResult.getResult() == null
&& jobResult.getFileId() == null
&& !jobResult.hasFiles()
&& jobResult.getError() == null) {
// If no result or error has been set, mark it as complete with an empty result
jobResult.completeWithResult("Task completed successfully");
@@ -186,7 +242,7 @@ public class TaskManager {
failedJobs++;
} else {
successfulJobs++;
if (result.getFileId() != null) {
if (result.hasFiles()) {
fileResultJobs++;
}
}
@@ -250,17 +306,8 @@ public class TaskManager {
&& result.getCompletedAt() != null
&& result.getCompletedAt().isBefore(expiryThreshold)) {
// If the job has a file result, delete the file
if (result.getFileId() != null) {
try {
fileStorage.deleteFile(result.getFileId());
} catch (Exception e) {
log.warn(
"Failed to delete file for job {}: {}",
entry.getKey(),
e.getMessage());
}
}
// Clean up file results
cleanupJobFiles(result, entry.getKey());
// Remove the job result
jobResults.remove(entry.getKey());
@@ -290,4 +337,128 @@ public class TaskManager {
cleanupExecutor.shutdownNow();
}
}
/** Check if a file is a ZIP file based on content type and filename */
private boolean isZipFile(String contentType, String fileName) {
if (contentType != null
&& (contentType.equals("application/zip")
|| contentType.equals("application/x-zip-compressed"))) {
return true;
}
if (fileName != null && fileName.toLowerCase().endsWith(".zip")) {
return true;
}
return false;
}
/** Extract a ZIP file into individual files and store them */
private List<ResultFile> extractZipToIndividualFiles(
String zipFileId, String originalZipFileName) throws IOException {
List<ResultFile> extractedFiles = new ArrayList<>();
MultipartFile zipFile = fileStorage.retrieveFile(zipFileId);
try (ZipInputStream zipIn =
ZipSecurity.createHardenedInputStream(new ByteArrayInputStream(zipFile.getBytes()))) {
ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
if (!entry.isDirectory()) {
// Use buffered reading for memory safety
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = zipIn.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
byte[] fileContent = out.toByteArray();
String contentType = determineContentType(entry.getName());
String individualFileId = fileStorage.storeBytes(fileContent, entry.getName());
ResultFile resultFile =
ResultFile.builder()
.fileId(individualFileId)
.fileName(entry.getName())
.contentType(contentType)
.fileSize(fileContent.length)
.build();
extractedFiles.add(resultFile);
log.debug(
"Extracted file: {} (size: {} bytes)",
entry.getName(),
fileContent.length);
}
zipIn.closeEntry();
}
}
// Clean up the original ZIP file after extraction
try {
fileStorage.deleteFile(zipFileId);
log.debug("Cleaned up original ZIP file: {}", zipFileId);
} catch (Exception e) {
log.warn("Failed to clean up original ZIP file {}: {}", zipFileId, e.getMessage());
}
return extractedFiles;
}
/** Determine content type based on file extension */
private String determineContentType(String fileName) {
if (fileName == null) {
return MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
String lowerName = fileName.toLowerCase();
if (lowerName.endsWith(".pdf")) {
return MediaType.APPLICATION_PDF_VALUE;
} else if (lowerName.endsWith(".txt")) {
return MediaType.TEXT_PLAIN_VALUE;
} else if (lowerName.endsWith(".json")) {
return MediaType.APPLICATION_JSON_VALUE;
} else if (lowerName.endsWith(".xml")) {
return MediaType.APPLICATION_XML_VALUE;
} else if (lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg")) {
return MediaType.IMAGE_JPEG_VALUE;
} else if (lowerName.endsWith(".png")) {
return MediaType.IMAGE_PNG_VALUE;
} else {
return MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
}
/** Clean up files associated with a job result */
private void cleanupJobFiles(JobResult result, String jobId) {
// Clean up all result files
if (result.hasFiles()) {
for (ResultFile resultFile : result.getAllResultFiles()) {
try {
fileStorage.deleteFile(resultFile.getFileId());
} catch (Exception e) {
log.warn(
"Failed to delete file {} for job {}: {}",
resultFile.getFileId(),
jobId,
e.getMessage());
}
}
}
}
/** Find the ResultFile metadata for a given file ID by searching through all job results */
public ResultFile findResultFileByFileId(String fileId) {
for (JobResult jobResult : jobResults.values()) {
if (jobResult.hasFiles()) {
for (ResultFile resultFile : jobResult.getAllResultFiles()) {
if (fileId.equals(resultFile.getFileId())) {
return resultFile;
}
}
}
}
return null;
}
}
@@ -154,11 +154,15 @@ public class TempFileCleanupService {
boolean containerMode = isContainerMode();
int unregisteredDeletedCount = cleanupUnregisteredFiles(containerMode, true, maxAgeMillis);
log.info(
"Scheduled cleanup complete. Deleted {} registered files, {} unregistered files, {} directories",
registeredDeletedCount,
unregisteredDeletedCount,
directoriesDeletedCount);
if (registeredDeletedCount > 0
|| unregisteredDeletedCount > 0
|| directoriesDeletedCount > 0) {
log.info(
"Scheduled cleanup complete. Deleted {} registered files, {} unregistered files, {} directories",
registeredDeletedCount,
unregisteredDeletedCount,
directoriesDeletedCount);
}
}
/**
@@ -166,7 +170,6 @@ public class TempFileCleanupService {
* important in Docker environments where temp files persist between container restarts.
*/
private void runStartupCleanup() {
log.info("Running startup temporary file cleanup");
boolean containerMode = isContainerMode();
log.info(
@@ -178,7 +181,6 @@ public class TempFileCleanupService {
long maxAgeMillis = containerMode ? 0 : 24 * 60 * 60 * 1000; // 0 or 24 hours
int totalDeletedCount = cleanupUnregisteredFiles(containerMode, false, maxAgeMillis);
log.info(
"Startup cleanup complete. Deleted {} temporary files/directories",
totalDeletedCount);
@@ -225,7 +227,7 @@ public class TempFileCleanupService {
tempDir -> {
try {
String phase = isScheduled ? "scheduled" : "startup";
log.info(
log.debug(
"Scanning directory for {} cleanup: {}",
phase,
tempDir);
@@ -0,0 +1,327 @@
package stirling.software.common.util;
import java.io.IOException;
import java.text.MessageFormat;
import lombok.extern.slf4j.Slf4j;
/**
* Utility class for handling exceptions with internationalized error messages. Provides consistent
* error handling and user-friendly messages across the application.
*/
@Slf4j
public class ExceptionUtils {
/**
* Create an IOException with internationalized message for PDF corruption.
*
* @param cause the original exception
* @return IOException with user-friendly message
*/
public static IOException createPdfCorruptedException(Exception cause) {
return createPdfCorruptedException(null, cause);
}
/**
* Create an IOException with internationalized message for PDF corruption with context.
*
* @param context additional context (e.g., "during merge", "during image extraction")
* @param cause the original exception
* @return IOException with user-friendly message
*/
public static IOException createPdfCorruptedException(String context, Exception cause) {
String message;
if (context != null && !context.isEmpty()) {
message =
String.format(
"Error %s: PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.",
context);
} else {
message =
"PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.";
}
return new IOException(message, cause);
}
/**
* Create an IOException with internationalized message for multiple corrupted PDFs.
*
* @param cause the original exception
* @return IOException with user-friendly message
*/
public static IOException createMultiplePdfCorruptedException(Exception cause) {
String message =
"One or more PDF files appear to be corrupted or damaged. Please try using the 'Repair PDF' feature on each file first before attempting to merge them.";
return new IOException(message, cause);
}
/**
* Create an IOException with internationalized message for PDF encryption issues.
*
* @param cause the original exception
* @return IOException with user-friendly message
*/
public static IOException createPdfEncryptionException(Exception cause) {
String message =
"The PDF appears to have corrupted encryption data. This can happen when the PDF was created with incompatible encryption methods. Please try using the 'Repair PDF' feature first, or contact the document creator for a new copy.";
return new IOException(message, cause);
}
/**
* Create an IOException with internationalized message for PDF password issues.
*
* @param cause the original exception
* @return IOException with user-friendly message
*/
public static IOException createPdfPasswordException(Exception cause) {
String message =
"The PDF Document is passworded and either the password was not provided or was incorrect";
return new IOException(message, cause);
}
/**
* Create an IOException with internationalized message for file processing errors.
*
* @param operation the operation being performed (e.g., "merge", "split", "convert")
* @param cause the original exception
* @return IOException with user-friendly message
*/
public static IOException createFileProcessingException(String operation, Exception cause) {
String message =
String.format(
"An error occurred while processing the file during %s operation: %s",
operation, cause.getMessage());
return new IOException(message, cause);
}
/**
* Create a generic IOException with internationalized message.
*
* @param messageKey the i18n message key
* @param defaultMessage the default message if i18n is not available
* @param cause the original exception
* @param args optional arguments for the message
* @return IOException with user-friendly message
*/
public static IOException createIOException(
String messageKey, String defaultMessage, Exception cause, Object... args) {
String message = MessageFormat.format(defaultMessage, args);
return new IOException(message, cause);
}
/**
* Create a generic RuntimeException with internationalized message.
*
* @param messageKey the i18n message key
* @param defaultMessage the default message if i18n is not available
* @param cause the original exception
* @param args optional arguments for the message
* @return RuntimeException with user-friendly message
*/
public static RuntimeException createRuntimeException(
String messageKey, String defaultMessage, Exception cause, Object... args) {
String message = MessageFormat.format(defaultMessage, args);
return new RuntimeException(message, cause);
}
/**
* Create an IllegalArgumentException with internationalized message.
*
* @param messageKey the i18n message key
* @param defaultMessage the default message if i18n is not available
* @param args optional arguments for the message
* @return IllegalArgumentException with user-friendly message
*/
public static IllegalArgumentException createIllegalArgumentException(
String messageKey, String defaultMessage, Object... args) {
String message = MessageFormat.format(defaultMessage, args);
return new IllegalArgumentException(message);
}
/** Create file validation exceptions. */
public static IllegalArgumentException createHtmlFileRequiredException() {
return createIllegalArgumentException(
"error.fileFormatRequired", "File must be in {0} format", "HTML or ZIP");
}
public static IllegalArgumentException createPdfFileRequiredException() {
return createIllegalArgumentException(
"error.fileFormatRequired", "File must be in {0} format", "PDF");
}
public static IllegalArgumentException createInvalidPageSizeException(String size) {
return createIllegalArgumentException(
"error.invalidFormat", "Invalid {0} format: {1}", "page size", size);
}
/** Create OCR-related exceptions. */
public static IOException createOcrLanguageRequiredException() {
return createIOException(
"error.optionsNotSpecified", "{0} options are not specified", null, "OCR language");
}
public static IOException createOcrInvalidLanguagesException() {
return createIOException(
"error.invalidFormat",
"Invalid {0} format: {1}",
null,
"OCR languages",
"none of the selected languages are valid");
}
public static IOException createOcrToolsUnavailableException() {
return createIOException(
"error.toolNotInstalled", "{0} is not installed", null, "OCR tools");
}
/** Create system requirement exceptions. */
public static IOException createPythonRequiredForWebpException() {
return createIOException(
"error.toolRequired", "{0} is required for {1}", null, "Python", "WebP conversion");
}
/** Create file operation exceptions. */
public static IOException createFileNotFoundException(String fileId) {
return createIOException("error.fileNotFound", "File not found with ID: {0}", null, fileId);
}
public static RuntimeException createPdfaConversionFailedException() {
return createRuntimeException(
"error.conversionFailed", "{0} conversion failed", null, "PDF/A");
}
public static IllegalArgumentException createInvalidComparatorException() {
return createIllegalArgumentException(
"error.invalidFormat",
"Invalid {0} format: {1}",
"comparator",
"only 'greater', 'equal', and 'less' are supported");
}
/** Create compression-related exceptions. */
public static RuntimeException createMd5AlgorithmException(Exception cause) {
return createRuntimeException(
"error.algorithmNotAvailable", "{0} algorithm not available", cause, "MD5");
}
public static IllegalArgumentException createCompressionOptionsException() {
return createIllegalArgumentException(
"error.optionsNotSpecified",
"{0} options are not specified",
"compression (expected output size and optimize level)");
}
public static IOException createGhostscriptCompressionException() {
return createIOException(
"error.commandFailed", "{0} command failed", null, "Ghostscript compression");
}
public static IOException createGhostscriptCompressionException(Exception cause) {
return createIOException(
"error.commandFailed", "{0} command failed", cause, "Ghostscript compression");
}
public static IOException createQpdfCompressionException(Exception cause) {
return createIOException("error.commandFailed", "{0} command failed", cause, "QPDF");
}
/**
* Check if an exception indicates a corrupted PDF and wrap it with appropriate message.
*
* @param e the exception to check
* @return the original exception if not PDF corruption, or a new IOException with user-friendly
* message
*/
public static IOException handlePdfException(IOException e) {
return handlePdfException(e, null);
}
/**
* Check if an exception indicates a corrupted PDF and wrap it with appropriate message.
*
* @param e the exception to check
* @param context additional context for the error
* @return the original exception if not PDF corruption, or a new IOException with user-friendly
* message
*/
public static IOException handlePdfException(IOException e, String context) {
if (PdfErrorUtils.isCorruptedPdfError(e)) {
return createPdfCorruptedException(context, e);
}
if (isEncryptionError(e)) {
return createPdfEncryptionException(e);
}
if (isPasswordError(e)) {
return createPdfPasswordException(e);
}
return e; // Return original exception if no specific handling needed
}
/**
* Check if an exception indicates a PDF encryption/decryption error.
*
* @param e the exception to check
* @return true if it's an encryption error, false otherwise
*/
public static boolean isEncryptionError(IOException e) {
String message = e.getMessage();
if (message == null) return false;
return message.contains("BadPaddingException")
|| message.contains("Given final block not properly padded")
|| message.contains("AES initialization vector not fully read")
|| message.contains("Failed to decrypt");
}
/**
* Check if an exception indicates a PDF password error.
*
* @param e the exception to check
* @return true if it's a password error, false otherwise
*/
public static boolean isPasswordError(IOException e) {
String message = e.getMessage();
if (message == null) return false;
return message.contains("password is incorrect")
|| message.contains("Password is not provided")
|| message.contains("PDF contains an encryption dictionary");
}
/**
* Log an exception with appropriate level based on its type.
*
* @param operation the operation being performed
* @param e the exception that occurred
*/
public static void logException(String operation, Exception e) {
if (PdfErrorUtils.isCorruptedPdfError(e)) {
log.warn("PDF corruption detected during {}: {}", operation, e.getMessage());
} else if (e instanceof IOException
&& (isEncryptionError((IOException) e) || isPasswordError((IOException) e))) {
log.info("PDF security issue during {}: {}", operation, e.getMessage());
} else {
log.error("Unexpected error during {}", operation, e);
}
}
/** Create common validation exceptions. */
public static IllegalArgumentException createInvalidArgumentException(String argumentName) {
return createIllegalArgumentException(
"error.invalidArgument", "Invalid argument: {0}", argumentName);
}
public static IllegalArgumentException createInvalidArgumentException(
String argumentName, String value) {
return createIllegalArgumentException(
"error.invalidFormat", "Invalid {0} format: {1}", argumentName, value);
}
public static IllegalArgumentException createNullArgumentException(String argumentName) {
return createIllegalArgumentException(
"error.argumentRequired", "{0} must not be null", argumentName);
}
}
@@ -32,21 +32,23 @@ public class FileToPdf {
try (TempFile tempOutputFile = new TempFile(tempFileManager, ".pdf")) {
try (TempFile tempInputFile =
new TempFile(tempFileManager, fileName.endsWith(".html") ? ".html" : ".zip")) {
new TempFile(
tempFileManager,
fileName.toLowerCase().endsWith(".html") ? ".html" : ".zip")) {
if (fileName.endsWith(".html")) {
if (fileName.toLowerCase().endsWith(".html")) {
String sanitizedHtml =
sanitizeHtmlContent(
new String(fileBytes, StandardCharsets.UTF_8), disableSanitize);
Files.write(
tempInputFile.getPath(),
sanitizedHtml.getBytes(StandardCharsets.UTF_8));
} else if (fileName.endsWith(".zip")) {
} else if (fileName.toLowerCase().endsWith(".zip")) {
Files.write(tempInputFile.getPath(), fileBytes);
sanitizeHtmlFilesInZip(
tempInputFile.getPath(), disableSanitize, tempFileManager);
} else {
throw new IllegalArgumentException("Unsupported file format: " + fileName);
throw ExceptionUtils.createHtmlFileRequiredException();
}
List<String> command = new ArrayList<>();
@@ -0,0 +1,55 @@
package stirling.software.common.util;
import java.io.IOException;
/** Utility class for detecting and handling PDF-related errors. */
public class PdfErrorUtils {
/**
* Checks if an IOException indicates a corrupted PDF file.
*
* @param e the IOException to check
* @return true if the error indicates PDF corruption, false otherwise
*/
public static boolean isCorruptedPdfError(IOException e) {
return isCorruptedPdfError(e.getMessage());
}
/**
* Checks if any Exception indicates a corrupted PDF file.
*
* @param e the Exception to check
* @return true if the error indicates PDF corruption, false otherwise
*/
public static boolean isCorruptedPdfError(Exception e) {
return isCorruptedPdfError(e.getMessage());
}
/**
* Checks if an error message indicates a corrupted PDF file.
*
* @param message the error message to check
* @return true if the message indicates PDF corruption, false otherwise
*/
private static boolean isCorruptedPdfError(String message) {
if (message == null) return false;
// Check for common corruption indicators
return message.contains("Missing root object specification")
|| message.contains("Header doesn't contain versioninfo")
|| message.contains("Expected trailer")
|| message.contains("Invalid PDF")
|| message.contains("Corrupted")
|| message.contains("damaged")
|| message.contains("Unknown dir object")
|| message.contains("Can't dereference COSObject")
|| message.contains("parseCOSString string should start with")
|| message.contains("ICCBased colorspace array must have a stream")
|| message.contains("1-based index not found")
|| message.contains("Invalid dictionary, found:")
|| message.contains("AES initialization vector not fully read")
|| message.contains("BadPaddingException")
|| message.contains("Given final block not properly padded")
|| message.contains("End-of-File, expected line");
}
}
@@ -42,26 +42,34 @@ public class PdfUtils {
public static PDRectangle textToPageSize(String size) {
switch (size.toUpperCase()) {
case "A0":
case "A0" -> {
return PDRectangle.A0;
case "A1":
}
case "A1" -> {
return PDRectangle.A1;
case "A2":
}
case "A2" -> {
return PDRectangle.A2;
case "A3":
}
case "A3" -> {
return PDRectangle.A3;
case "A4":
}
case "A4" -> {
return PDRectangle.A4;
case "A5":
}
case "A5" -> {
return PDRectangle.A5;
case "A6":
}
case "A6" -> {
return PDRectangle.A6;
case "LETTER":
}
case "LETTER" -> {
return PDRectangle.LETTER;
case "LEGAL":
}
case "LEGAL" -> {
return PDRectangle.LEGAL;
default:
throw new IllegalArgumentException("Invalid standard page size: " + size);
}
default -> throw ExceptionUtils.createInvalidPageSizeException(size);
}
}
@@ -135,6 +143,17 @@ public class PdfUtils {
int DPI,
String filename)
throws IOException, Exception {
// Validate and limit DPI to prevent excessive memory usage
final int MAX_SAFE_DPI = 500; // Maximum safe DPI to prevent memory issues
if (DPI > MAX_SAFE_DPI) {
throw ExceptionUtils.createIllegalArgumentException(
"error.dpiExceedsLimit",
"DPI value {0} exceeds maximum safe limit of {1}. High DPI values can cause memory issues and crashes. Please use a lower DPI value.",
DPI,
MAX_SAFE_DPI);
}
try (PDDocument document = pdfDocumentFactory.load(inputStream)) {
PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true);
@@ -158,7 +177,21 @@ public class PdfUtils {
writer.prepareWriteSequence(null);
for (int i = 0; i < pageCount; ++i) {
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
BufferedImage image;
try {
image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null
&& e.getMessage()
.contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigForDpi",
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).",
i + 1,
DPI);
}
throw e;
}
writer.writeToSequence(new IIOImage(image, null, null), param);
}
@@ -190,7 +223,20 @@ public class PdfUtils {
PdfImageDimensionValue dimension = pageSizes.get(settings);
if (dimension == null) {
// Render the image to get the dimensions
pdfSizeImage = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
try {
pdfSizeImage = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null
&& e.getMessage()
.contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigExceedsArray",
"PDF page {0} is too large to render at {1} DPI. The resulting image would exceed Java's maximum array size. Please try a lower DPI value (recommended: 150 or less).",
i + 1,
DPI);
}
throw e;
}
pdfSizeImageIndex = i;
dimension =
new PdfImageDimensionValue(
@@ -218,7 +264,20 @@ public class PdfUtils {
if (firstImageAlreadyRendered && i == 0) {
pageImage = pdfSizeImage;
} else {
pageImage = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
try {
pageImage = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null
&& e.getMessage()
.contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigForDpi",
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).",
i + 1,
DPI);
}
throw e;
}
}
// Calculate the x-coordinate to center the image
@@ -238,7 +297,20 @@ public class PdfUtils {
// Zip the images and return as byte array
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
for (int i = 0; i < pageCount; ++i) {
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
BufferedImage image;
try {
image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null
&& e.getMessage().contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigForDpi",
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).",
i + 1,
DPI);
}
throw e;
}
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
ImageIO.write(image, imageType, baosImage);
@@ -276,7 +348,19 @@ public class PdfUtils {
PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
BufferedImage bim;
try {
bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null
&& e.getMessage().contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigFor300Dpi",
"PDF page {0} is too large to render at 300 DPI. The resulting image would exceed Java's maximum array size. Please use a lower DPI value for PDF-to-image conversion.",
page + 1);
}
throw e;
}
PDPage originalPage = document.getPage(page);
float width = originalPage.getMediaBox().getWidth();
@@ -349,7 +433,7 @@ public class PdfUtils {
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
doc.save(byteArrayOutputStream);
log.info("PDF successfully saved to byte array");
log.debug("PDF successfully saved to byte array");
return byteArrayOutputStream.toByteArray();
}
}
@@ -495,8 +579,7 @@ public class PdfUtils {
case "less":
return actualPageCount < pageCount;
default:
throw new IllegalArgumentException(
"Invalid comparator. Only 'greater', 'equal', and 'less' are supported.");
throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
}
}
@@ -84,6 +84,16 @@ public class ProcessExecutor {
.getProcessExecutor()
.getSessionLimit()
.getCalibreSessionLimit();
case GHOSTSCRIPT ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getGhostscriptSessionLimit();
case OCR_MY_PDF ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getOcrMyPdfSessionLimit();
};
long timeoutMinutes =
@@ -128,6 +138,16 @@ public class ProcessExecutor {
.getProcessExecutor()
.getTimeoutMinutes()
.getCalibreTimeoutMinutes();
case GHOSTSCRIPT ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getGhostscriptTimeoutMinutes();
case OCR_MY_PDF ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getOcrMyPdfTimeoutMinutes();
};
return new ProcessExecutor(semaphoreLimit, liveUpdates, timeoutMinutes);
});
@@ -278,7 +298,9 @@ public class ProcessExecutor {
INSTALL_APP,
CALIBRE,
TESSERACT,
QPDF
QPDF,
GHOSTSCRIPT,
OCR_MY_PDF
}
public class ProcessExecutorResult {
@@ -108,6 +108,12 @@ public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
} catch (IllegalArgumentException ie) {
log.info("text not supported by font ");
font = checkSupportedFontForCharacter(unicodeText);
} catch (UnsupportedOperationException ue) {
log.info(
"font does not support encoding operation: {} for text: '{}'",
font.getClass().getSimpleName(),
unicodeText);
font = checkSupportedFontForCharacter(unicodeText);
} finally {
// if any other font is not supported, then replace default character *
if (font == null) {
@@ -126,7 +126,7 @@ class AutoJobPostMappingIntegrationTest {
verify(jobExecutorService).runJobGeneric(
asyncCaptor.capture(),
workCaptor.capture(),
workCaptor.capture(),
timeoutCaptor.capture(),
queueableCaptor.capture(),
resourceWeightCaptor.capture());
@@ -197,7 +197,7 @@ class AutoJobPostMappingIntegrationTest {
autoJobAspect.wrapWithJobExecution(joinPoint, autoJobPostMapping);
// Then
assertEquals("stored-file-id", pdfFile.getFileId(),
assertEquals("stored-file-id", pdfFile.getFileId(),
"FileId should be set to the stored file id");
assertNotNull(pdfFile.getFileInput(), "FileInput should be replaced with persistent file");
@@ -187,4 +187,4 @@ class FileStorageTest {
// Assert
assertFalse(result);
}
}
}
@@ -64,8 +64,8 @@ class JobExecutorServiceTest {
void setUp() {
// Initialize the service manually with all its dependencies
jobExecutorService = new JobExecutorService(
taskManager,
fileStorage,
taskManager,
fileStorage,
request,
resourceMonitor,
jobQueue,
@@ -199,4 +199,4 @@ class JobExecutorServiceTest {
assertTrue(e.getCause() instanceof TimeoutException);
}
}
}
}
@@ -36,7 +36,7 @@ class JobQueueTest {
// Mark stubbing as lenient to avoid UnnecessaryStubbingException
lenient().when(resourceMonitor.calculateDynamicQueueCapacity(anyInt(), anyInt())).thenReturn(10);
lenient().when(resourceMonitor.getCurrentStatus()).thenReturn(statusRef);
// Initialize JobQueue with mocked ResourceMonitor
jobQueue = new JobQueue(resourceMonitor);
}
@@ -99,4 +99,4 @@ class JobQueueTest {
assertTrue(jobQueue.isJobQueued(jobId));
assertFalse(jobQueue.isJobQueued("nonexistent"));
}
}
}
@@ -117,7 +117,7 @@ class ResourceMonitorTest {
// Then
assertEquals(shouldQueue, result,
String.format("For weight %d and status %s, shouldQueue should be %s",
String.format("For weight %d and status %s, shouldQueue should be %s",
weight, status, shouldQueue));
}
@@ -134,4 +134,4 @@ class ResourceMonitorTest {
assertTrue(staleMetrics.isStale(5000), "Metrics from 6 seconds ago should be stale with 5s threshold");
assertFalse(freshMetrics.isStale(5000), "Fresh metrics should not be stale");
}
}
}
@@ -18,6 +18,7 @@ import org.springframework.test.util.ReflectionTestUtils;
import stirling.software.common.model.job.JobResult;
import stirling.software.common.model.job.JobStats;
import stirling.software.common.model.job.ResultFile;
class TaskManagerTest {
@@ -73,13 +74,17 @@ class TaskManagerTest {
}
@Test
void testSetFileResult() {
void testSetFileResult() throws Exception {
// Arrange
String jobId = UUID.randomUUID().toString();
taskManager.createTask(jobId);
String fileId = "file-id";
String originalFileName = "test.pdf";
String contentType = "application/pdf";
long fileSize = 1024L;
// Mock the fileStorage.getFileSize() call
when(fileStorage.getFileSize(fileId)).thenReturn(fileSize);
// Act
taskManager.setFileResult(jobId, fileId, originalFileName, contentType);
@@ -88,9 +93,17 @@ class TaskManagerTest {
JobResult result = taskManager.getJobResult(jobId);
assertNotNull(result);
assertTrue(result.isComplete());
assertEquals(fileId, result.getFileId());
assertEquals(originalFileName, result.getOriginalFileName());
assertEquals(contentType, result.getContentType());
assertTrue(result.hasFiles());
assertFalse(result.hasMultipleFiles());
var resultFiles = result.getAllResultFiles();
assertEquals(1, resultFiles.size());
ResultFile resultFile = resultFiles.get(0);
assertEquals(fileId, resultFile.getFileId());
assertEquals(originalFileName, resultFile.getFileName());
assertEquals(contentType, resultFile.getContentType());
assertEquals(fileSize, resultFile.getFileSize());
assertNotNull(result.getCompletedAt());
}
@@ -163,8 +176,11 @@ class TaskManagerTest {
}
@Test
void testGetJobStats() {
void testGetJobStats() throws Exception {
// Arrange
// Mock fileStorage.getFileSize for file operations
when(fileStorage.getFileSize("file-id")).thenReturn(1024L);
// 1. Create active job
String activeJobId = "active-job";
taskManager.createTask(activeJobId);
@@ -216,9 +232,15 @@ class TaskManagerTest {
LocalDateTime oldTime = LocalDateTime.now().minusHours(1);
ReflectionTestUtils.setField(oldJob, "completedAt", oldTime);
ReflectionTestUtils.setField(oldJob, "complete", true);
ReflectionTestUtils.setField(oldJob, "fileId", "file-id");
ReflectionTestUtils.setField(oldJob, "originalFileName", "test.pdf");
ReflectionTestUtils.setField(oldJob, "contentType", "application/pdf");
// Create a ResultFile and set it using the new approach
ResultFile resultFile = ResultFile.builder()
.fileId("file-id")
.fileName("test.pdf")
.contentType("application/pdf")
.fileSize(1024L)
.build();
ReflectionTestUtils.setField(oldJob, "resultFiles", java.util.List.of(resultFile));
when(fileStorage.deleteFile("file-id")).thenReturn(true);
@@ -252,17 +274,17 @@ class TaskManagerTest {
// Verify the executor service is shutdown
// This is difficult to test directly, but we can verify it doesn't throw exceptions
}
@Test
void testAddNote() {
// Arrange
String jobId = UUID.randomUUID().toString();
taskManager.createTask(jobId);
String note = "Test note";
// Act
boolean result = taskManager.addNote(jobId, note);
// Assert
assertTrue(result);
JobResult jobResult = taskManager.getJobResult(jobId);
@@ -271,16 +293,16 @@ class TaskManagerTest {
assertEquals(1, jobResult.getNotes().size());
assertEquals(note, jobResult.getNotes().get(0));
}
@Test
void testAddNote_NonExistentJob() {
// Arrange
String jobId = "non-existent-job";
String note = "Test note";
// Act
boolean result = taskManager.addNote(jobId, note);
// Assert
assertFalse(result);
}
@@ -82,10 +82,10 @@ public class TempFileCleanupServiceTest {
when(tempFileManagement.isStartupCleanup()).thenReturn(false);
when(tempFileManagement.isCleanupSystemTemp()).thenReturn(false);
when(tempFileManagement.getCleanupIntervalMinutes()).thenReturn(30L);
// Set machineType using reflection (still needed for this field)
ReflectionTestUtils.setField(cleanupService, "machineType", "Standard");
when(tempFileManager.getMaxAgeMillis()).thenReturn(3600000L); // 1 hour
}
@@ -113,30 +113,30 @@ public class TempFileCleanupServiceTest {
Path ourTempFile3 = Files.createFile(customTempDir.resolve("stirling-pdf-789.tmp"));
Path ourTempFile4 = Files.createFile(customTempDir.resolve("pdf-save-123-456.tmp"));
Path ourTempFile5 = Files.createFile(libreOfficeTempDir.resolve("input_file.pdf"));
// Old temporary files
Path oldTempFile = Files.createFile(systemTempDir.resolve("output_old.pdf"));
// System temp files that should be cleaned in container mode
Path sysTempFile1 = Files.createFile(systemTempDir.resolve("lu123abc.tmp"));
Path sysTempFile2 = Files.createFile(customTempDir.resolve("ocr_process123"));
Path sysTempFile3 = Files.createFile(customTempDir.resolve("tmp_upload.tmp"));
// Files that should be preserved
Path jettyFile1 = Files.createFile(systemTempDir.resolve("jetty-123.tmp"));
Path jettyFile2 = Files.createFile(systemTempDir.resolve("something-with-jetty-inside.tmp"));
Path regularFile = Files.createFile(systemTempDir.resolve("important.txt"));
// Create a nested directory with temp files
Path nestedDir = Files.createDirectories(systemTempDir.resolve("nested"));
Path nestedTempFile = Files.createFile(nestedDir.resolve("output_nested.pdf"));
// Empty file (special case)
Path emptyFile = Files.createFile(systemTempDir.resolve("empty.tmp"));
// Configure mock registry to say these files aren't registered
when(registry.contains(any(File.class))).thenReturn(false);
// The set of files that will be deleted in our test
Set<Path> deletedFiles = new HashSet<>();
@@ -145,31 +145,31 @@ public class TempFileCleanupServiceTest {
// Mock Files.list for each directory we'll process
mockedFiles.when(() -> Files.list(eq(systemTempDir)))
.thenReturn(Stream.of(
ourTempFile1, ourTempFile2, oldTempFile, sysTempFile1,
ourTempFile1, ourTempFile2, oldTempFile, sysTempFile1,
jettyFile1, jettyFile2, regularFile, emptyFile, nestedDir));
mockedFiles.when(() -> Files.list(eq(customTempDir)))
.thenReturn(Stream.of(ourTempFile3, ourTempFile4, sysTempFile2, sysTempFile3));
mockedFiles.when(() -> Files.list(eq(libreOfficeTempDir)))
.thenReturn(Stream.of(ourTempFile5));
mockedFiles.when(() -> Files.list(eq(nestedDir)))
.thenReturn(Stream.of(nestedTempFile));
// Configure Files.isDirectory for each path
mockedFiles.when(() -> Files.isDirectory(eq(nestedDir))).thenReturn(true);
mockedFiles.when(() -> Files.isDirectory(any(Path.class))).thenReturn(false);
// Configure Files.exists to return true for all paths
mockedFiles.when(() -> Files.exists(any(Path.class))).thenReturn(true);
// Configure Files.getLastModifiedTime to return different times based on file names
mockedFiles.when(() -> Files.getLastModifiedTime(any(Path.class)))
.thenAnswer(invocation -> {
Path path = invocation.getArgument(0);
String fileName = path.getFileName().toString();
// For files with "old" in the name, return a timestamp older than maxAgeMillis
if (fileName.contains("old")) {
return FileTime.fromMillis(System.currentTimeMillis() - 5000000);
@@ -183,13 +183,13 @@ public class TempFileCleanupServiceTest {
return FileTime.fromMillis(System.currentTimeMillis() - 60000); // 1 minute ago
}
});
// Configure Files.size to return different sizes based on file names
mockedFiles.when(() -> Files.size(any(Path.class)))
.thenAnswer(invocation -> {
Path path = invocation.getArgument(0);
String fileName = path.getFileName().toString();
// Return 0 bytes for the empty file
if (fileName.equals("empty.tmp")) {
return 0L;
@@ -199,7 +199,7 @@ public class TempFileCleanupServiceTest {
return 1024L; // 1 KB
}
});
// For deleteIfExists, track which files would be deleted
mockedFiles.when(() -> Files.deleteIfExists(any(Path.class)))
.thenAnswer(invocation -> {
@@ -207,28 +207,28 @@ public class TempFileCleanupServiceTest {
deletedFiles.add(path);
return true;
});
// Act - set containerMode to false for this test
invokeCleanupDirectoryStreaming(systemTempDir, false, 0, 3600000);
invokeCleanupDirectoryStreaming(customTempDir, false, 0, 3600000);
invokeCleanupDirectoryStreaming(libreOfficeTempDir, false, 0, 3600000);
// Assert - Only old temp files and empty files should be deleted
assertTrue(deletedFiles.contains(oldTempFile), "Old temp file should be deleted");
assertTrue(deletedFiles.contains(emptyFile), "Empty file should be deleted");
// Regular temp files should not be deleted because they're too new
assertFalse(deletedFiles.contains(ourTempFile1), "Recent temp file should be preserved");
assertFalse(deletedFiles.contains(ourTempFile2), "Recent temp file should be preserved");
assertFalse(deletedFiles.contains(ourTempFile3), "Recent temp file should be preserved");
assertFalse(deletedFiles.contains(ourTempFile4), "Recent temp file should be preserved");
assertFalse(deletedFiles.contains(ourTempFile5), "Recent temp file should be preserved");
// System temp files should not be deleted in non-container mode
assertFalse(deletedFiles.contains(sysTempFile1), "System temp file should be preserved in non-container mode");
assertFalse(deletedFiles.contains(sysTempFile2), "System temp file should be preserved in non-container mode");
assertFalse(deletedFiles.contains(sysTempFile3), "System temp file should be preserved in non-container mode");
// Jetty files and regular files should never be deleted
assertFalse(deletedFiles.contains(jettyFile1), "Jetty file should be preserved");
assertFalse(deletedFiles.contains(jettyFile2), "File with jetty in name should be preserved");
@@ -242,10 +242,10 @@ public class TempFileCleanupServiceTest {
Path ourTempFile = Files.createFile(systemTempDir.resolve("output_123.pdf"));
Path sysTempFile = Files.createFile(systemTempDir.resolve("lu123abc.tmp"));
Path regularFile = Files.createFile(systemTempDir.resolve("important.txt"));
// Configure mock registry to say these files aren't registered
when(registry.contains(any(File.class))).thenReturn(false);
// The set of files that will be deleted in our test
Set<Path> deletedFiles = new HashSet<>();
@@ -254,21 +254,21 @@ public class TempFileCleanupServiceTest {
// Mock Files.list for systemTempDir
mockedFiles.when(() -> Files.list(eq(systemTempDir)))
.thenReturn(Stream.of(ourTempFile, sysTempFile, regularFile));
// Configure Files.isDirectory
mockedFiles.when(() -> Files.isDirectory(any(Path.class))).thenReturn(false);
// Configure Files.exists
mockedFiles.when(() -> Files.exists(any(Path.class))).thenReturn(true);
// Configure Files.getLastModifiedTime to return recent timestamps
mockedFiles.when(() -> Files.getLastModifiedTime(any(Path.class)))
.thenReturn(FileTime.fromMillis(System.currentTimeMillis() - 60000)); // 1 minute ago
// Configure Files.size to return normal size
mockedFiles.when(() -> Files.size(any(Path.class)))
.thenReturn(1024L); // 1 KB
// For deleteIfExists, track which files would be deleted
mockedFiles.when(() -> Files.deleteIfExists(any(Path.class)))
.thenAnswer(invocation -> {
@@ -276,10 +276,10 @@ public class TempFileCleanupServiceTest {
deletedFiles.add(path);
return true;
});
// Act - set containerMode to true and maxAgeMillis to 0 for container startup cleanup
invokeCleanupDirectoryStreaming(systemTempDir, true, 0, 0);
// Assert - In container mode, both our temp files and system temp files should be deleted
// regardless of age (when maxAgeMillis is 0)
assertTrue(deletedFiles.contains(ourTempFile), "Our temp file should be deleted in container mode");
@@ -293,10 +293,10 @@ public class TempFileCleanupServiceTest {
// Arrange - Create an empty file
Path emptyFile = Files.createFile(systemTempDir.resolve("empty.tmp"));
Path recentEmptyFile = Files.createFile(systemTempDir.resolve("recent_empty.tmp"));
// Configure mock registry to say these files aren't registered
when(registry.contains(any(File.class))).thenReturn(false);
// The set of files that will be deleted in our test
Set<Path> deletedFiles = new HashSet<>();
@@ -305,19 +305,19 @@ public class TempFileCleanupServiceTest {
// Mock Files.list for systemTempDir
mockedFiles.when(() -> Files.list(eq(systemTempDir)))
.thenReturn(Stream.of(emptyFile, recentEmptyFile));
// Configure Files.isDirectory
mockedFiles.when(() -> Files.isDirectory(any(Path.class))).thenReturn(false);
// Configure Files.exists
mockedFiles.when(() -> Files.exists(any(Path.class))).thenReturn(true);
// Configure Files.getLastModifiedTime to return different times based on file names
mockedFiles.when(() -> Files.getLastModifiedTime(any(Path.class)))
.thenAnswer(invocation -> {
Path path = invocation.getArgument(0);
String fileName = path.getFileName().toString();
if (fileName.equals("empty.tmp")) {
// More than 5 minutes old
return FileTime.fromMillis(System.currentTimeMillis() - 6 * 60 * 1000);
@@ -326,11 +326,11 @@ public class TempFileCleanupServiceTest {
return FileTime.fromMillis(System.currentTimeMillis() - 2 * 60 * 1000);
}
});
// Configure Files.size to return 0 for empty files
mockedFiles.when(() -> Files.size(any(Path.class)))
.thenReturn(0L);
// For deleteIfExists, track which files would be deleted
mockedFiles.when(() -> Files.deleteIfExists(any(Path.class)))
.thenAnswer(invocation -> {
@@ -338,14 +338,14 @@ public class TempFileCleanupServiceTest {
deletedFiles.add(path);
return true;
});
// Act
invokeCleanupDirectoryStreaming(systemTempDir, false, 0, 3600000);
// Assert
assertTrue(deletedFiles.contains(emptyFile),
assertTrue(deletedFiles.contains(emptyFile),
"Empty file older than 5 minutes should be deleted");
assertFalse(deletedFiles.contains(recentEmptyFile),
assertFalse(deletedFiles.contains(recentEmptyFile),
"Empty file newer than 5 minutes should not be deleted");
}
}
@@ -356,14 +356,14 @@ public class TempFileCleanupServiceTest {
Path dir1 = Files.createDirectories(systemTempDir.resolve("dir1"));
Path dir2 = Files.createDirectories(dir1.resolve("dir2"));
Path dir3 = Files.createDirectories(dir2.resolve("dir3"));
Path tempFile1 = Files.createFile(dir1.resolve("output_1.pdf"));
Path tempFile2 = Files.createFile(dir2.resolve("output_2.pdf"));
Path tempFile3 = Files.createFile(dir3.resolve("output_old_3.pdf"));
// Configure mock registry to say these files aren't registered
when(registry.contains(any(File.class))).thenReturn(false);
// The set of files that will be deleted in our test
Set<Path> deletedFiles = new HashSet<>();
@@ -372,16 +372,16 @@ public class TempFileCleanupServiceTest {
// Mock Files.list for each directory
mockedFiles.when(() -> Files.list(eq(systemTempDir)))
.thenReturn(Stream.of(dir1));
mockedFiles.when(() -> Files.list(eq(dir1)))
.thenReturn(Stream.of(tempFile1, dir2));
mockedFiles.when(() -> Files.list(eq(dir2)))
.thenReturn(Stream.of(tempFile2, dir3));
mockedFiles.when(() -> Files.list(eq(dir3)))
.thenReturn(Stream.of(tempFile3));
// Configure Files.isDirectory for each path
mockedFiles.when(() -> Files.isDirectory(eq(dir1))).thenReturn(true);
mockedFiles.when(() -> Files.isDirectory(eq(dir2))).thenReturn(true);
@@ -389,16 +389,16 @@ public class TempFileCleanupServiceTest {
mockedFiles.when(() -> Files.isDirectory(eq(tempFile1))).thenReturn(false);
mockedFiles.when(() -> Files.isDirectory(eq(tempFile2))).thenReturn(false);
mockedFiles.when(() -> Files.isDirectory(eq(tempFile3))).thenReturn(false);
// Configure Files.exists to return true for all paths
mockedFiles.when(() -> Files.exists(any(Path.class))).thenReturn(true);
// Configure Files.getLastModifiedTime to return different times based on file names
mockedFiles.when(() -> Files.getLastModifiedTime(any(Path.class)))
.thenAnswer(invocation -> {
Path path = invocation.getArgument(0);
String fileName = path.getFileName().toString();
if (fileName.contains("old")) {
// Old file
return FileTime.fromMillis(System.currentTimeMillis() - 5000000);
@@ -407,11 +407,11 @@ public class TempFileCleanupServiceTest {
return FileTime.fromMillis(System.currentTimeMillis() - 60000);
}
});
// Configure Files.size to return normal size
mockedFiles.when(() -> Files.size(any(Path.class)))
.thenReturn(1024L);
// For deleteIfExists, track which files would be deleted
mockedFiles.when(() -> Files.deleteIfExists(any(Path.class)))
.thenAnswer(invocation -> {
@@ -419,14 +419,14 @@ public class TempFileCleanupServiceTest {
deletedFiles.add(path);
return true;
});
// Act
invokeCleanupDirectoryStreaming(systemTempDir, false, 0, 3600000);
// Debug - print what was deleted
System.out.println("Deleted files: " + deletedFiles);
System.out.println("Looking for: " + tempFile3);
// Assert
assertFalse(deletedFiles.contains(tempFile1), "Recent temp file should be preserved");
assertFalse(deletedFiles.contains(tempFile2), "Recent temp file should be preserved");
@@ -437,28 +437,28 @@ public class TempFileCleanupServiceTest {
/**
* Helper method to invoke the private cleanupDirectoryStreaming method using reflection
*/
private void invokeCleanupDirectoryStreaming(Path directory, boolean containerMode, int depth, long maxAgeMillis)
private void invokeCleanupDirectoryStreaming(Path directory, boolean containerMode, int depth, long maxAgeMillis)
throws IOException {
try {
// Create a consumer that tracks deleted files
AtomicInteger deleteCount = new AtomicInteger(0);
Consumer<Path> deleteCallback = path -> deleteCount.incrementAndGet();
// Get the method with updated signature
var method = TempFileCleanupService.class.getDeclaredMethod(
"cleanupDirectoryStreaming",
"cleanupDirectoryStreaming",
Path.class, boolean.class, int.class, long.class, boolean.class, Consumer.class);
method.setAccessible(true);
// Invoke the method with appropriate parameters
method.invoke(cleanupService, directory, containerMode, depth, maxAgeMillis, false, deleteCallback);
} catch (Exception e) {
throw new RuntimeException("Error invoking cleanupDirectoryStreaming", e);
}
}
// Matcher for exact path equality
private static Path eq(Path path) {
return argThat(arg -> arg != null && arg.equals(path));
}
}
}
@@ -27,7 +27,7 @@ public class FileToPdfTest {
String fileName = "test.html"; // Sample file name indicating an HTML file
boolean disableSanitize = false; // Flag to control sanitization
TempFileManager tempFileManager = mock(TempFileManager.class); // Mock TempFileManager
// Mock the temp file creation to return real temp files
try {
when(tempFileManager.createTempFile(anyString()))
@@ -22,7 +22,7 @@ class SpringContextHolderTest {
void testSetApplicationContext() {
// Act
contextHolder.setApplicationContext(mockApplicationContext);
// Assert
assertTrue(SpringContextHolder.isInitialized());
}
@@ -33,10 +33,10 @@ class SpringContextHolderTest {
contextHolder.setApplicationContext(mockApplicationContext);
TestBean expectedBean = new TestBean();
when(mockApplicationContext.getBean(TestBean.class)).thenReturn(expectedBean);
// Act
TestBean result = SpringContextHolder.getBean(TestBean.class);
// Assert
assertSame(expectedBean, result);
verify(mockApplicationContext).getBean(TestBean.class);
@@ -46,10 +46,10 @@ class SpringContextHolderTest {
@Test
void testGetBean_ApplicationContextNotSet() {
// Don't set application context
// Act
TestBean result = SpringContextHolder.getBean(TestBean.class);
// Assert
assertNull(result);
}
@@ -59,10 +59,10 @@ class SpringContextHolderTest {
// Arrange
contextHolder.setApplicationContext(mockApplicationContext);
when(mockApplicationContext.getBean(TestBean.class)).thenThrow(new org.springframework.beans.BeansException("Bean not found") {});
// Act
TestBean result = SpringContextHolder.getBean(TestBean.class);
// Assert
assertNull(result);
}
@@ -70,4 +70,4 @@ class SpringContextHolderTest {
// Simple test class
private static class TestBean {
}
}
}
View File
+29 -133
View File
@@ -2,38 +2,21 @@
## 1. Introduction
Stirling-PDF is a robust, locally hosted, web-based PDF manipulation tool. **Stirling 2.0** represents a complete frontend rewrite, replacing the legacy Thymeleaf-based UI with a modern React SPA (Single Page Application).
This guide focuses on developing for Stirling 2.0, including both the React frontend and Spring Boot backend development workflows.
Stirling-PDF is a robust, locally hosted, web-based PDF manipulation tool. This guide focuses on Docker-based development and testing, which is the recommended approach for working with the full version of Stirling-PDF.
## 2. Project Overview
**Stirling 2.0** is built using:
Stirling-PDF is built using:
**Backend:**
- Spring Boot (Java 17+, JDK 21 recommended)
- PDFBox for core PDF operations
- LibreOffice for document conversions
- qpdf for PDF optimization
- Spring Security (optional, controlled by `DOCKER_ENABLE_SECURITY`)
- Lombok for reducing boilerplate code
**Frontend (React SPA):**
- React + TypeScript
- Vite for build tooling and development server
- Mantine UI component library
- TailwindCSS for styling
- PDF.js for client-side PDF rendering
- PDF-LIB.js for client-side PDF manipulation
- IndexedDB for client-side file storage and thumbnails
- i18next for internationalization
**Infrastructure:**
- Docker for containerization
- Gradle for build management
**Legacy (reference only during development):**
- Thymeleaf templates (being completely replaced in 2.0)
- Spring Boot + Thymeleaf
- PDFBox
- LibreOffice
- qpdf
- HTML, CSS, JavaScript
- Docker
- PDF.js
- PDF-LIB.js
- Lombok
## 3. Development Environment Setup
@@ -41,8 +24,7 @@ This guide focuses on developing for Stirling 2.0, including both the React fron
- Docker
- Git
- Java JDK 17 or later (JDK 21 recommended)
- Node.js 18+ and npm (required for frontend development)
- Java JDK 17 or later
- Gradle 7.0 or later (Included within the repo)
### Setup Steps
@@ -74,45 +56,15 @@ Visit the [Lombok website](https://projectlombok.org/setup/) for installation in
5. Add environment variable
For local testing, you should generally be testing the full 'Security' version of Stirling PDF. To do this, you must add the environment flag DISABLE_ADDITIONAL_FEATURES=false to your system and/or IDE build/run step.
5. **Frontend Setup (Required for Stirling 2.0)**
Navigate to the frontend directory and install dependencies using npm.
## 4. Stirling 2.0 Development Workflow
### Frontend Development (React)
The frontend is a React SPA that runs independently during development:
1. **Start the backend**: Run the Spring Boot application (serves API endpoints on localhost:8080)
2. **Start the frontend dev server**: Navigate to the frontend directory and run the development server (serves UI on localhost:5173)
3. **Development flow**: The Vite dev server automatically proxies API calls to the backend
### File Storage Architecture
Stirling 2.0 uses client-side file storage:
- **IndexedDB**: Stores files locally in the browser with automatic thumbnail generation
- **PDF.js**: Handles client-side PDF rendering and processing
- **URL Parameters**: Support for deep linking and tool state persistence
### Legacy Code Reference
The existing Thymeleaf templates remain in the codebase during development as reference material but will be completely removed for the 2.0 release.
## 5. Project Structure
## 4. Project Structure
```bash
Stirling-PDF/
├── .github/ # GitHub-specific files (workflows, issue templates)
├── configs/ # Configuration files used by stirling at runtime (generated at runtime)
├── frontend/ # React SPA frontend (Stirling 2.0)
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── tools/ # Tool-specific React components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── services/ # API and utility services
│ │ ├── types/ # TypeScript type definitions
│ │ └── utils/ # Utility functions
│ ├── public/
│ │ └── locales/ # Internationalization files (JSON)
│ ├── package.json # Frontend dependencies
│ └── vite.config.ts # Vite configuration
├── cucumber/ # Cucumber test files
│ ├── features/
├── customFiles/ # Custom static files and templates (generated at runtime used to replace existing files)
├── docs/ # Documentation files
├── exampleYmlFiles/ # Example YAML configuration files
@@ -132,14 +84,16 @@ Stirling-PDF/
│ │ │ ├── service/
│ │ │ └── utils/
│ │ └── resources/
│ │ ├── static/ # Legacy static assets (reference only)
│ │ ├── static/
│ │ │ ├── css/
│ │ │ ├── js/
│ │ │ └── pdfjs/
│ │ └── templates/ # Legacy Thymeleaf templates (reference only)
│ │ └── templates/
│ └── test/
├── testing/ # Cucumber and integration tests
└── cucumber/ # Cucumber test files
│ └── java/
└── stirling/
│ └── software/
│ └── SPDF/
├── build.gradle # Gradle build configuration
├── Dockerfile # Main Dockerfile
├── Dockerfile.ultra-lite # Dockerfile for ultra-lite version
@@ -148,7 +102,7 @@ Stirling-PDF/
└── test.sh # Test script to deploy all docker versions and run cuke tests
```
## 6. Docker-based Development
## 5. Docker-based Development
Stirling-PDF offers several Docker versions:
@@ -248,7 +202,7 @@ Stirling-PDF uses different Docker images for various configurations. The build
Note: The `--no-cache` and `--pull` flags ensure that the build process uses the latest base images and doesn't use cached layers, which is useful for testing and ensuring reproducible builds. however to improve build times these can often be removed depending on your usecase
## 7. Testing
## 6. Testing
### Comprehensive Testing Script
@@ -274,15 +228,6 @@ Note: The `test.sh` script will run automatically when you raise a PR. However,
2. Access the application at `http://localhost:8080` and manually test all features developed.
### Frontend Development Testing (Stirling 2.0)
For React frontend development:
1. Start the backend: Run the Spring Boot application to serve API endpoints on localhost:8080
2. Start the frontend dev server: Navigate to the frontend directory and run the development server on localhost:5173
3. The Vite dev server automatically proxies API calls to the backend
4. Test React components, UI interactions, and IndexedDB file operations using browser developer tools
### Local Testing (Java and UI Components)
For quick iterations and development of Java backend, JavaScript, and UI components, you can run and test Stirling-PDF locally without Docker. This approach allows you to work on and verify changes to:
@@ -313,7 +258,7 @@ Important notes:
- There are currently no automated unit tests. All testing is done manually through the UI or API calls. (You are welcome to add JUnits!)
- Always verify your changes in the full Docker environment before submitting pull requests, as some integrations and features will only work in the complete setup.
## 8. Contributing
## 7. Contributing
1. Fork the repository on GitHub.
2. Create a new branch for your feature or bug fix.
@@ -327,7 +272,7 @@ Important notes:
6. Push your changes to your fork.
7. Submit a pull request to the main repository.
8. See additional [contributing guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md).
8. See additional [contributing guidelines](../CONTRIBUTING.md).
When you raise a PR:
@@ -338,11 +283,11 @@ When you raise a PR:
Address any issues that arise from these checks before finalizing your pull request.
## 9. API Documentation
## 8. API Documentation
API documentation is available at `/swagger-ui/index.html` when running the application. You can also view the latest API documentation [here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/).
## 10. Customization
## 9. Customization
Stirling-PDF can be customized through environment variables or a `settings.yml` file. Key customization options include:
@@ -361,7 +306,7 @@ docker run -p 8080:8080 -e APP_NAME="My PDF Tool" stirling-pdf:full
Refer to the main README for a full list of customization options.
## 11. Language Translations
## 10. Language Translations
For managing language translations that affect multiple files, Stirling-PDF provides a helper script:
@@ -381,56 +326,7 @@ Remember to test your changes thoroughly to ensure they don't break any existing
## Code examples
### React Component Development (Stirling 2.0)
For Stirling 2.0, new features are built as React components instead of Thymeleaf templates:
#### Creating a New Tool Component
1. **Create the React Component:**
```typescript
// frontend/src/tools/NewTool.tsx
import { useState } from 'react';
import { Button, FileInput, Container } from '@mantine/core';
interface NewToolProps {
params: Record<string, any>;
updateParams: (updates: Record<string, any>) => void;
}
export default function NewTool({ params, updateParams }: NewToolProps) {
const [files, setFiles] = useState<File[]>([]);
const handleProcess = async () => {
// Process files using API or client-side logic
};
return (
<Container>
<FileInput
multiple
accept="application/pdf"
onChange={setFiles}
/>
<Button onClick={handleProcess}>Process</Button>
</Container>
);
}
```
2. **Add API Integration:**
```typescript
// Use existing API endpoints or create new ones
const response = await fetch('/api/v1/new-tool', {
method: 'POST',
body: formData
});
```
3. **Register in Tool Picker:**
Update the tool picker component to include the new tool with proper routing and URL parameter support.
### Legacy Reference: Overview of Thymeleaf
### Overview of Thymeleaf
Thymeleaf is a server-side Java HTML template engine. It is used in Stirling-PDF to render dynamic web pages. Thymeleaf integrates heavily with Spring Boot.
+66
View File
@@ -0,0 +1,66 @@
# Exception Handling Guide
This guide outlines the common error handling patterns used within Stirling-PDF and provides tips for internationalising error messages. The examples cover the main languages found in the project: Java, JavaScript, HTML/CSS, and a small amount of Python.
## General Principles
- **Fail fast and log clearly.** Exceptions should provide enough information for debugging without exposing sensitive data.
- **Use consistent user messages.** Text shown to users must be pulled from the localisation files so that translations are centrally managed.
- **Avoid silent failures.** Always log unexpected errors and provide the user with a helpful message.
## Java
Java forms the core of Stirling-PDF. When adding new features or handling errors:
1. **Create custom exceptions** to represent specific failure cases. This keeps the code self-documenting and easier to handle at higher levels.
2. **Use `try-with-resources`** when working with streams or other closable resources to ensure clean-up even on failure.
3. **Return meaningful HTTP status codes** in controllers by throwing `ResponseStatusException` or using `@ExceptionHandler` methods.
4. **Log with context** using the projects logging framework. Include identifiers or IDs that help trace the issue.
5. **Internationalise messages** by placing user-facing text in `messages_en_GB.properties` and referencing them with message keys.
## JavaScript
On the client side, JavaScript handles form validation and user interactions.
- Use `try`/`catch` around asynchronous operations (e.g., `fetch`) and display a translated error notice if the call fails.
- Validate input before sending it to the server and provide inline feedback with messages from the translation files.
- Log unexpected errors to the browser console for easier debugging, but avoid revealing sensitive information.
## HTML & CSS
HTML templates should reserve a space for displaying error messages. Example pattern:
```html
<div class="error" role="alert" th:text="${errorMessage}"></div>
```
Use CSS classes (e.g., `.error`) to style the message so it is clearly visible and accessible. Keep the markup simple to ensure screen readers can announce the error correctly.
## Python
Python scripts in this project are mainly for utility tasks. Follow these guidelines:
- Wrap file operations or external calls in `try`/`except` blocks.
- Print or log errors in a consistent format. If the script outputs messages to end users, ensure they are translatable.
Example:
```python
try:
perform_task()
except Exception as err:
logger.error("Task failed: %s", err)
print(gettext("task.error"))
```
## Internationalisation (i18n)
All user-visible error strings should be defined in the main translation file (`messages_en_GB.properties`). Other language files will use the same keys. Refer to messages in code rather than hard-coding text.
When creating new messages:
1. Add the English phrase to `messages_en_GB.properties`.
2. Reference the message key in your Java, JavaScript, or Python code.
3. Update other localisation files as needed.
Following these patterns helps keep Stirling-PDF stable, easier to debug, and friendly to users in all supported languages.
+29
View File
@@ -0,0 +1,29 @@
# Developer Guide Directory
This directory contains all development-related documentation for Stirling PDF.
## 📚 Documentation Index
### Core Development
- **[DeveloperGuide.md](./DeveloperGuide.md)** - Main developer setup and architecture guide
- **[EXCEPTION_HANDLING_GUIDE.md](./EXCEPTION_HANDLING_GUIDE.md)** - Exception handling patterns and i18n best practices
- **[HowToAddNewLanguage.md](./HowToAddNewLanguage.md)** - Internationalization and translation guide
### Features & Documentation
- **[AGENTS.md](./AGENTS.md)** - Agent-based functionality documentation
- **[USERS.md](./USERS.md)** - User-focused documentation and guides
## 🔗 Related Files in Root
- **[README.md](../README.md)** - Project overview and quick start
- **[CONTRIBUTING.md](../CONTRIBUTING.md)** - Contribution guidelines
- **[SECURITY.md](../SECURITY.md)** - Security policies and reporting
- **[DATABASE.md](../DATABASE.md)** - Database setup and configuration (usage guide)
- **[HowToUseOCR.md](../HowToUseOCR.md)** - OCR setup and configuration (usage guide)
## 📝 Contributing to Documentation
When adding new development documentation:
1. Place technical guides in this `devGuide/` directory
2. Update this index file with a brief description
3. Keep user-facing docs (README, CONTRIBUTING, SECURITY) in the root
4. Follow existing naming conventions (PascalCase for guides)
View File
-67
View File
@@ -1,67 +0,0 @@
# Docker Setup for Stirling-PDF
This directory contains the organized Docker configurations for the split frontend/backend architecture.
## Directory Structure
```
docker/
├── backend/ # Backend Docker files
│ ├── Dockerfile # Standard backend
│ ├── Dockerfile.ultra-lite # Minimal backend
│ └── Dockerfile.fat # Full-featured backend
├── frontend/ # Frontend Docker files
│ ├── Dockerfile # React/Vite frontend with nginx
│ ├── nginx.conf # Nginx configuration
│ └── entrypoint.sh # Dynamic backend URL setup
├── monolith/ # Single container setup
│ ├── Dockerfile # Combined frontend + backend
│ ├── nginx-monolith.conf # Nginx config for monolith
│ └── start-monolith.sh # Startup script
└── compose/ # Docker Compose files
├── docker-compose.yml # Standard setup
├── docker-compose.ultra-lite.yml # Ultra-lite setup
├── docker-compose.fat.yml # Full-featured setup
└── docker-compose.monolith.yml # Single container setup
```
## Usage
### Separate Containers (Recommended)
From the project root directory:
```bash
# Standard version
docker-compose -f docker/compose/docker-compose.yml up --build
# Ultra-lite version
docker-compose -f docker/compose/docker-compose.ultra-lite.yml up --build
# Fat version
docker-compose -f docker/compose/docker-compose.fat.yml up --build
```
### Single Container (Monolith)
```bash
# Single container with both frontend and backend
docker-compose -f docker/compose/docker-compose.monolith.yml up --build
```
## Access Points
- **Frontend**: http://localhost:3000
- **Backend API (debugging)**: http://localhost:8080 (TODO: Remove in production)
- **Backend API (via frontend)**: http://localhost:3000/api/*
## Configuration
- **Backend URL**: Set `BACKEND_URL` environment variable for custom backend locations
- **Custom Ports**: Modify port mappings in docker-compose files
- **Memory Limits**: Adjust memory limits per variant (2G ultra-lite, 4G standard, 6G fat)
## Development vs Production
- **Development**: Keep backend port 8080 exposed for debugging
- **Production**: Remove backend port exposure, use only frontend proxy
-66
View File
@@ -1,66 +0,0 @@
version: '3.8'
services:
backend:
build:
context: ../..
dockerfile: docker/backend/Dockerfile.fat
container_name: stirling-pdf-backend-fat
restart: on-failure:5
deploy:
resources:
limits:
memory: 6G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- "8080:8080" # TODO: Remove in production - for debugging only
expose:
- "8080"
volumes:
- ../../stirling/latest/data:/usr/share/tessdata:rw
- ../../stirling/latest/config:/configs:rw
- ../../stirling/latest/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "false"
SECURITY_ENABLELOGIN: "false"
FAT_DOCKER: "true"
INSTALL_BOOK_AND_ADVANCED_HTML_OPS: "false"
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Full-featured Stirling-PDF with all capabilities
UI_APPNAMENAVBAR: Stirling-PDF Fat
SYSTEM_MAXFILESIZE: "200"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
SHOW_SURVEY: "true"
networks:
- stirling-network
frontend:
build:
context: ../../frontend
dockerfile: ../docker/frontend/Dockerfile
container_name: stirling-pdf-frontend-fat
restart: on-failure:5
ports:
- "3000:80"
environment:
BACKEND_URL: http://backend:8080
depends_on:
- backend
networks:
- stirling-network
networks:
stirling-network:
driver: bridge
volumes:
stirling-data:
stirling-config:
stirling-logs:
@@ -1,42 +0,0 @@
version: '3.8'
services:
stirling-pdf-monolith:
build:
context: ../..
dockerfile: docker/monolith/Dockerfile
container_name: stirling-pdf-monolith
restart: on-failure:5
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:80/ && curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
interval: 10s
timeout: 15s
retries: 16
ports:
- "3000:80" # Frontend access
- "8080:8080" # Direct backend access (for debugging)
volumes:
- ../../stirling/latest/data:/usr/share/tessdata:rw
- ../../stirling/latest/config:/configs:rw
- ../../stirling/latest/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false"
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Single container Stirling-PDF
UI_APPNAMENAVBAR: Stirling-PDF Monolith
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
SHOW_SURVEY: "true"
volumes:
stirling-data:
stirling-config:
stirling-logs:
@@ -1,56 +0,0 @@
version: '3.8'
services:
backend:
build:
context: ../..
dockerfile: docker/backend/Dockerfile.ultra-lite
container_name: stirling-pdf-backend-ultra-lite
restart: on-failure:5
deploy:
resources:
limits:
memory: 2G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- "8080:8080" # TODO: Remove in production - for debugging only
expose:
- "8080"
volumes:
- ../../stirling/latest/config:/configs:rw
- ../../stirling/latest/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false"
ENDPOINTS_GROUPS_TO_REMOVE: "CLI"
LANGS: "en_GB,en_US"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Ultra-lite version of Stirling-PDF
UI_APPNAMENAVBAR: Stirling-PDF Ultra-lite
SYSTEM_MAXFILESIZE: "50"
networks:
- stirling-network
frontend:
build:
context: ../../frontend
dockerfile: ../docker/frontend/Dockerfile
container_name: stirling-pdf-frontend-ultra-lite
restart: on-failure:5
ports:
- "3000:80"
environment:
BACKEND_URL: http://backend:8080
depends_on:
- backend
networks:
- stirling-network
networks:
stirling-network:
driver: bridge
-64
View File
@@ -1,64 +0,0 @@
version: '3.8'
services:
backend:
build:
context: ../..
dockerfile: docker/backend/Dockerfile
container_name: stirling-pdf-backend
restart: on-failure:5
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- "8080:8080" # TODO: Remove in production - for debugging only
expose:
- "8080"
volumes:
- ../../stirling/latest/data:/usr/share/tessdata:rw
- ../../stirling/latest/config:/configs:rw
- ../../stirling/latest/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false"
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest
UI_APPNAMENAVBAR: Stirling-PDF Latest
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
SHOW_SURVEY: "true"
networks:
- stirling-network
frontend:
build:
context: ../../frontend
dockerfile: ../docker/frontend/Dockerfile
container_name: stirling-pdf-frontend
restart: on-failure:5
ports:
- "3000:80"
environment:
BACKEND_URL: http://backend:8080
depends_on:
- backend
networks:
- stirling-network
networks:
stirling-network:
driver: bridge
volumes:
stirling-data:
stirling-config:
stirling-logs:
-38
View File
@@ -1,38 +0,0 @@
# Frontend Dockerfile - React/Vite application
FROM node:20-alpine AS build
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Build the application
RUN npm run build
# Production stage
FROM nginx:alpine
# Copy built files from build stage
COPY --from=build /app/dist /usr/share/nginx/html
# Copy nginx configuration and entrypoint
COPY nginx.conf /etc/nginx/nginx.conf
COPY entrypoint.sh /entrypoint.sh
# Make entrypoint executable
RUN chmod +x /entrypoint.sh
# Expose port 80 (standard HTTP port)
EXPOSE 80
# Environment variables for flexibility
ENV BACKEND_URL=http://backend:8080
# Use custom entrypoint
ENTRYPOINT ["/entrypoint.sh"]
-10
View File
@@ -1,10 +0,0 @@
#!/bin/sh
# Set default backend URL if not provided
BACKEND_URL=${BACKEND_URL:-"http://backend:8080"}
# Replace the placeholder in nginx.conf with the actual backend URL
sed -i "s|\${BACKEND_URL}|${BACKEND_URL}|g" /etc/nginx/nginx.conf
# Start nginx
exec nginx -g "daemon off;"
-49
View File
@@ -1,49 +0,0 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html index.htm;
# Handle client-side routing - support subpaths
location / {
try_files $uri $uri/ /index.html;
}
# Proxy API calls to backend
location /api/ {
proxy_pass ${BACKEND_URL}/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
}
-128
View File
@@ -1,128 +0,0 @@
# Monolith Dockerfile - Frontend + Backend in same container
# Build frontend
FROM node:20-alpine AS frontend-build
WORKDIR /app/frontend
# Copy frontend package files
COPY frontend/package*.json ./
# Install frontend dependencies
RUN npm ci
# Copy frontend source
COPY frontend/ ./
# Build frontend
RUN npm run build
# Main stage - Backend with frontend files
FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715
# Copy necessary files
COPY scripts /scripts
COPY pipeline /pipeline
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
COPY stirling-pdf/build/libs/*.jar app.jar
# Copy built frontend files
COPY --from=frontend-build /app/frontend/dist /usr/share/nginx/html
ARG VERSION_TAG
LABEL org.opencontainers.image.title="Stirling-PDF Monolith"
LABEL org.opencontainers.image.description="Single container with both frontend and backend for Stirling-PDF"
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.vendor="Stirling-Tools"
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
LABEL maintainer="Stirling-Tools"
LABEL org.opencontainers.image.authors="Stirling-Tools"
LABEL org.opencontainers.image.version="${VERSION_TAG}"
LABEL org.opencontainers.image.keywords="PDF, manipulation, monolith, single-container"
# Set Environment Variables
ENV DISABLE_ADDITIONAL_FEATURES=true \
VERSION_TAG=$VERSION_TAG \
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \
UMASK=022 \
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
UNO_PATH=/usr/lib/libreoffice/program \
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
PATH=$PATH:/opt/venv/bin \
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
TMPDIR=/tmp/stirling-pdf \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf
# Install nginx and all dependencies
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk upgrade --no-cache -a && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
bash \
curl \
qpdf \
shadow \
su-exec \
openssl \
openssl-dev \
openjdk21-jre \
nginx \
# Doc conversion
gcompat \
libc6-compat \
libreoffice \
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced features)
tesseract-ocr-data-eng \
tesseract-ocr-data-chi_sim \
tesseract-ocr-data-deu \
tesseract-ocr-data-fra \
tesseract-ocr-data-por \
# CV
py3-opencv \
python3 \
py3-pip \
py3-pillow@testing \
py3-pdf2image@testing && \
python3 -m venv /opt/venv && \
/opt/venv/bin/pip install --upgrade pip setuptools && \
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
fc-cache -f -v && \
chmod +x /scripts/* && \
chmod +x /scripts/init.sh && \
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /tmp/stirling-pdf && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/nginx/html
# Copy nginx configuration for monolith
COPY docker/monolith/nginx-monolith.conf /etc/nginx/nginx.conf
# Copy startup script
COPY docker/monolith/start-monolith.sh /start-monolith.sh
RUN chmod +x /start-monolith.sh
# Expose both ports
EXPOSE 80 8080
# Set user and run command
ENTRYPOINT ["tini", "--"]
CMD ["/start-monolith.sh"]
-49
View File
@@ -1,49 +0,0 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html index.htm;
# Handle client-side routing - support subpaths
location / {
try_files $uri $uri/ /index.html;
}
# Proxy API calls to backend running on same container
location /api/ {
proxy_pass http://localhost:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
}
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# Start the Java backend in the background
echo "Starting Java backend..."
su-exec stirlingpdfuser:stirlingpdfgroup bash -c "
cd /home/stirlingpdfuser && \
java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar &
/opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1 &
"
# Wait for backend to start
echo "Waiting for backend to start..."
until curl -f http://localhost:8080/api/v1/info/status >/dev/null 2>&1; do
sleep 2
done
echo "Backend started, starting nginx..."
# Start nginx in the foreground
nginx -g "daemon off;"
@@ -17,7 +17,6 @@ services:
- ./stirling/latest/config:/configs:rw
- ./stirling/latest/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF-Ultra-lite
@@ -18,7 +18,6 @@ services:
- ./stirling/latest/config:/configs:rw
- ./stirling/latest/logs:/logs:rw
environment:
DISABLE_ADDITIONAL_FEATURES: "true"
SECURITY_ENABLELOGIN: "false"
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
SYSTEM_DEFAULTLOCALE: en-US
-24
View File
@@ -1,24 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
/dist
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
-70
View File
@@ -1,70 +0,0 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
-22
View File
@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using Vite"
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<title>Vite App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.jsx"></script>
</body>
</html>
-5886
View File
File diff suppressed because it is too large Load Diff
-67
View File
@@ -1,67 +0,0 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:8080",
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mantine/core": "^8.0.1",
"@mantine/dropzone": "^8.0.1",
"@mantine/hooks": "^8.0.1",
"@mui/icons-material": "^7.1.0",
"@mui/material": "^7.1.0",
"@tailwindcss/postcss": "^4.1.8",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
"autoprefixer": "^10.4.21",
"axios": "^1.9.0",
"i18next": "^25.2.1",
"i18next-browser-languagedetector": "^8.1.0",
"i18next-http-backend": "^3.0.2",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^3.11.174",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-i18next": "^15.5.2",
"react-router-dom": "^7.6.0",
"tailwindcss": "^4.1.8",
"web-vitals": "^2.1.4"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react": "^19.1.4",
"@types/react-dom": "^19.1.5",
"@vitejs/plugin-react": "^4.5.0",
"postcss": "^8.5.3",
"postcss-cli": "^11.0.1",
"postcss-preset-mantine": "^1.17.0",
"postcss-simple-vars": "^7.0.1",
"typescript": "^5.8.3",
"vite": "^6.3.5"
}
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
plugins: [
require('@tailwindcss/postcss'),
require('autoprefixer'),
],
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

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