Compare commits

..

57 Commits

Author SHA1 Message Date
a
f7add727a6 Merge branch 'filePath' of git@github.com:Stirling-Tools/Stirling-PDF.git into filePath 2025-01-06 12:38:43 +00:00
Anthony Stirling
cd947c2554 formatting 2025-01-06 12:38:13 +00:00
Anthony Stirling
9422a30ebf Merge branch 'main' into filePath 2025-01-04 18:11:39 +00:00
github-actions[bot]
e754e6012a 📝 Update README: Translation Progress Table (#2608)
Auto-generated by [create-pull-request][1]

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

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-04 18:07:06 +00:00
Ludy
3227da55e0 Bump docker-compose from v2.29.1 to v2.32.1 (#2600)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-04 18:06:51 +00:00
Ludy
709a79c3d9 Update labeler-config.yml (#2601)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-04 18:06:40 +00:00
github-actions[bot]
77bb15bc8b Update 3rd Party Licenses (#2604)
Auto-generated by [create-pull-request][1]

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

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-04 18:06:15 +00:00
github-actions[bot]
beaa86cbf9 Update translation files (#2609)
Auto-generated by [create-pull-request][1]

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

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-04 18:06:02 +00:00
Sai Kumar
b8303e3860 Pdf to image custom page selection (#2576)
# Description

Implemented custom page selection for the pdf-to-image feature, allowing
users to specify which PDF pages to convert to images.

1. Variable Renaming: Changed singleOrMultiple to imageResultType
because it supports three options: single, multiple, and custom.
2. New Field: Added pageNumbers to accept user-defined page selections.
This field appears only when custom is selected in the UI.
3. New Method: Added getPageIndicesToConvert to process and validate the
specified page numbers.
4. Method Update: Updated convertFromPdf to handle custom page numbers,
ensuring only selected pages are converted.
5. Translation Properties: Added two new English translation properties,
custom and customPageNumber, to all language files with placeholder
values. These will need to be translated into country-specific languages
in the future.

Note: If an invalid page number is provided (zero, negative, or exceeds
page count), a single image containing all PDF pages is generated.

Closes #918 

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [x] I have attached images of the change if it is UI based
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [x] My changes generate no new warnings
- [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)

![Screenshot 2025-01-02 at 12 31
29 AM](https://github.com/user-attachments/assets/c4ba3f31-5dd6-4a17-991e-51b86c2eb466)
![Screenshot 2025-01-02 at 12 31
49 AM](https://github.com/user-attachments/assets/3e800a95-2088-4f69-8a01-bd03d7b9e471)

---------

Co-authored-by: Sai Kumar J <saikumar@Sais-MacBook-Air.local>
Co-authored-by: Ludy <Ludy87@users.noreply.github.com>
Co-authored-by: saikumar <saikumar.jetti@gmail.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2025-01-04 18:01:13 +00:00
Anthony Stirling
ed273a6e92 space 2025-01-04 15:13:58 +00:00
Anthony Stirling
0136c25e1d remove logs 2025-01-04 13:12:34 +00:00
Anthony Stirling
cf03bdc17b test 2025-01-04 12:56:20 +00:00
Anthony Stirling
093b882141 null 2025-01-04 12:46:01 +00:00
Anthony Stirling
46507f10b6 null checks 2025-01-04 12:41:29 +00:00
a
54fbf666fd Merge branch 'filePath' of git@github.com:Stirling-Tools/Stirling-PDF.git into filePath 2025-01-04 12:31:47 +00:00
Anthony Stirling
faecaf1ee4 debugs 2025-01-04 12:30:58 +00:00
Anthony Stirling
a062b36ee5 Update build.yml 2025-01-04 11:34:41 +00:00
Anthony Stirling
00b6d60309 Merge branch 'main' into filePath 2025-01-04 00:48:12 +00:00
pixeebot[bot]
1e0121b4d6 Hardening suggestions for Stirling-PDF / filePath (#2606)
I've reviewed the recently opened PR ([2605 - File paths dynamic
](https://github.com/Stirling-Tools/Stirling-PDF/pull/2605)) 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%7C73ac17942fc6504dd1d4e9a8c779e26aba813ac9)

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2025-01-04 00:38:41 +00:00
Anthony Stirling
5ba98e4411 Revert "removes unused implementation com.unboundid.product.scim2" (#2607)
Reverts Stirling-Tools/Stirling-PDF#2599
2025-01-04 00:27:43 +00:00
Anthony Stirling
d7a3708a13 Merge branch 'main' into filePath 2025-01-04 00:20:25 +00:00
Anthony Stirling
73ac17942f finals 2025-01-04 00:19:56 +00:00
Ludy
116dfcc065 removes unused implementation com.unboundid.product.scim2 (#2599)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [x] My changes generate no new warnings
- [ ] 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)
2025-01-04 00:09:39 +00:00
Anthony Stirling
54e599a18b Test 2025-01-04 00:09:00 +00:00
Ludy
2d76927b3c fixes: untrusted code (#2598)
# Description


https://github.com/Stirling-Tools/Stirling-PDF/security/code-scanning/151

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-03 12:40:51 +00:00
Ludy
41c269f208 Bump gradle-version from 8.7 to 8.12 (#2596)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [x] My changes generate no new warnings
- [ ] 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)
2025-01-03 11:26:40 +00:00
reecebrowne
38633d4db1 Green buttons (#2597)
# Description

Making buttons green
Closes #()

![image](https://github.com/user-attachments/assets/29417e03-664d-4a77-a3ca-80d0a8b76f07)

## Checklist

- [X ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ X] I have performed a self-review of my own code
- [X ] I have attached images of the change if it is UI based
- [ X] I have commented my code, particularly in hard-to-understand
areas
- [ X] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ X] My changes generate no new warnings
- [ 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)

Co-authored-by: Reece Browne <reece@stirling.pdf>
2025-01-03 11:23:26 +00:00
github-actions[bot]
0ff45c656a 📝 Update README: Translation Progress Table (#2593)
Auto-generated by [create-pull-request][1]

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

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-02 20:39:00 +00:00
Ludy
bc282c6aa5 Update PR-Demo-Comment.yml (#2594)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 20:38:45 +00:00
albanobattistella
50575bc80b Update messages_it_IT.properties (#2591)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 20:22:35 +00:00
Ludy
6f04f01c2b changes the permission deploy-pr (#2592)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 20:21:27 +00:00
Ludy
e80eaaa6d1 changes the permission to write issues (#2590)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 19:42:24 +00:00
github-actions[bot]
01288dafe8 📝 Update README: Translation Progress Table (#2587)
Auto-generated by [create-pull-request][1]

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

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ludy <Ludy87@users.noreply.github.com>
2025-01-02 18:43:44 +00:00
Anthony Stirling
e3c7b6f955 Update README.md 2025-01-02 18:41:33 +00:00
Ludy
875f5a85ef Token-Permissions & Pinned-Dependencies (#2586)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 18:22:14 +00:00
swanemar
ef174a1e8a additional Swedish translations (#2585)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [X] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [X] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [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)
2025-01-02 18:21:39 +00:00
Anthony Stirling
958c214605 Update build.gradle 2025-01-02 17:48:27 +00:00
Ludy
56ab9c3eee Update messages_de_DE.properties (#2583)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 17:38:23 +00:00
Ludy
7b768db969 Fix: Translation Verification hard-coded repo properties (#2584)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 17:28:32 +00:00
github-actions[bot]
36c4d8552d 📝 Update README: Translation Progress Table (#2580)
Auto-generated by [create-pull-request][1]

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

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-02 15:58:24 +00:00
Ludy
4544fb7211 Fix: Translation Verification (#2581)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2025-01-02 15:58:08 +00:00
github-actions[bot]
807639308a Update translation files (#2582)
Auto-generated by [create-pull-request][1]

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

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-02 15:54:07 +00:00
reecebrowne
49fb634690 Feature/improved signature element (#2489)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
Co-authored-by: Reece Browne <reece@stirling.pdf>
2025-01-02 15:49:29 +00:00
github-actions[bot]
715efca25d Update translation files (#2579)
Auto-generated by [create-pull-request][1]

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

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-02 15:01:30 +00:00
dependabot[bot]
a4c988c7b2 Bump gradle from 8.11-jdk17 to 8.12-jdk17 (#2556)
Bumps gradle from 8.11-jdk17 to 8.12-jdk17.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=gradle&package-manager=docker&previous-version=8.11-jdk17&new-version=8.12-jdk17)](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-01-02 14:55:42 +00:00
Ludy
f45de05c99 Fixes SSO login rejection (#2566)
# Description

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [x] My changes generate no new warnings
- [ ] 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)
2025-01-02 14:52:07 +00:00
Omar Ahmed Hassan
d5faddbc85 Enhancement: Enhance NFunction evaluation and support advanced NFunctions (#2577)
# Description

Enhance NFunction sanitization and support advanced functions:
- Start page counting from 1 rather than 0 as PDFs are one based from
the user's perspective, thus functions results would be affected by
starting with "0" rather than "1".
- Ignore out of bound results rather than stopping iterations to work
with functions such as (n - 4) when page count is 10 as we would get
positive values when n > 4.
- Remove spaces to support expressions such as 2n + 1 rather just 2n+1.
- Support advanced functions as follows:
- Support expressions such as follows 5(n-1), n(n-1), expressions
followed by opening rounded without '*' operator.
- Support expressions such as follows (n-1)5, (n-1)n, expressions
preceded closing rounded without '*' operator.
- Support consecutive "n" expressions, examples: nnn, 2nn, nn*3, nnnn.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [x] My changes generate no new warnings
- [ ] 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)
2025-01-02 14:48:20 +00:00
dependabot[bot]
5e173b92d4 Bump peter-evans/create-pull-request from 7.0.5 to 7.0.6 (#2574)
Bumps
[peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request)
from 7.0.5 to 7.0.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/peter-evans/create-pull-request/releases">peter-evans/create-pull-request's
releases</a>.</em></p>
<blockquote>
<h2>Create Pull Request v7.0.6</h2>
<p>⚙️ Fixes an issue with commit signing where unicode characters in
file paths were not preserved.</p>
<h2>What's Changed</h2>
<ul>
<li>build(deps-dev): bump <code>@​vercel/ncc</code> from 0.38.1 to
0.38.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3365">peter-evans/create-pull-request#3365</a></li>
<li>Update distribution by <a
href="https://github.com/actions-bot"><code>@​actions-bot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3370">peter-evans/create-pull-request#3370</a></li>
<li>build(deps): bump
<code>@​octokit/plugin-rest-endpoint-methods</code> from 13.2.4 to
13.2.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3375">peter-evans/create-pull-request#3375</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.50 to
18.19.54 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3376">peter-evans/create-pull-request#3376</a></li>
<li>build(deps): bump <code>@​octokit/plugin-paginate-rest</code> from
11.3.3 to 11.3.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3377">peter-evans/create-pull-request#3377</a></li>
<li>Update distribution by <a
href="https://github.com/actions-bot"><code>@​actions-bot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3388">peter-evans/create-pull-request#3388</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.54 to
18.19.55 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3400">peter-evans/create-pull-request#3400</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.10.1 to 1.11.1
by <a href="https://github.com/dependabot"><code>@​dependabot</code></a>
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3401">peter-evans/create-pull-request#3401</a></li>
<li>build(deps): bump
<code>@​octokit/plugin-rest-endpoint-methods</code> from 13.2.5 to
13.2.6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3403">peter-evans/create-pull-request#3403</a></li>
<li>build(deps-dev): bump eslint-plugin-import from 2.30.0 to 2.31.0 by
<a href="https://github.com/dependabot"><code>@​dependabot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3402">peter-evans/create-pull-request#3402</a></li>
<li>build(deps): bump <code>@​octokit/plugin-throttling</code> from
9.3.1 to 9.3.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3404">peter-evans/create-pull-request#3404</a></li>
<li>Update distribution by <a
href="https://github.com/actions-bot"><code>@​actions-bot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3423">peter-evans/create-pull-request#3423</a></li>
<li>build(deps-dev): bump typescript from 5.6.2 to 5.6.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3441">peter-evans/create-pull-request#3441</a></li>
<li>build(deps): bump undici from 6.19.8 to 6.20.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3442">peter-evans/create-pull-request#3442</a></li>
<li>Update distribution by <a
href="https://github.com/actions-bot"><code>@​actions-bot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3451">peter-evans/create-pull-request#3451</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.55 to
18.19.58 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3457">peter-evans/create-pull-request#3457</a></li>
<li>build(deps-dev): bump <code>@​types/jest</code> from 29.5.13 to
29.5.14 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3462">peter-evans/create-pull-request#3462</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.58 to
18.19.60 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3463">peter-evans/create-pull-request#3463</a></li>
<li>chore: don't bundle undici by <a
href="https://github.com/benmccann"><code>@​benmccann</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3475">peter-evans/create-pull-request#3475</a></li>
<li>Update distribution by <a
href="https://github.com/actions-bot"><code>@​actions-bot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3478">peter-evans/create-pull-request#3478</a></li>
<li>chore: use node-fetch-native support for proxy env vars by <a
href="https://github.com/peter-evans"><code>@​peter-evans</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3483">peter-evans/create-pull-request#3483</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.60 to
18.19.64 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3488">peter-evans/create-pull-request#3488</a></li>
<li>build(deps-dev): bump undici from 6.20.1 to 6.21.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3499">peter-evans/create-pull-request#3499</a></li>
<li>build(deps-dev): bump <code>@​vercel/ncc</code> from 0.38.2 to
0.38.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3500">peter-evans/create-pull-request#3500</a></li>
<li>docs: note <code>push-to-repo</code> classic PAT
<code>workflow</code> scope requirement by <a
href="https://github.com/scop"><code>@​scop</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3511">peter-evans/create-pull-request#3511</a></li>
<li>docs: spelling fixes by <a
href="https://github.com/scop"><code>@​scop</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3512">peter-evans/create-pull-request#3512</a></li>
<li>build(deps-dev): bump typescript from 5.6.3 to 5.7.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3516">peter-evans/create-pull-request#3516</a></li>
<li>build(deps-dev): bump prettier from 3.3.3 to 3.4.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3517">peter-evans/create-pull-request#3517</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.64 to
18.19.66 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3518">peter-evans/create-pull-request#3518</a></li>
<li>docs(README): clarify that an existing open PR is managed by <a
href="https://github.com/caugner"><code>@​caugner</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3498">peter-evans/create-pull-request#3498</a></li>
<li>Update distribution by <a
href="https://github.com/actions-bot"><code>@​actions-bot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3529">peter-evans/create-pull-request#3529</a></li>
<li>build(deps): bump <code>@​octokit/plugin-paginate-rest</code> from
11.3.5 to 11.3.6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3542">peter-evans/create-pull-request#3542</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.66 to
18.19.67 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3543">peter-evans/create-pull-request#3543</a></li>
<li>build(deps-dev): bump prettier from 3.4.0 to 3.4.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3544">peter-evans/create-pull-request#3544</a></li>
<li>build(deps-dev): bump eslint-import-resolver-typescript from 3.6.3
to 3.7.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3559">peter-evans/create-pull-request#3559</a></li>
<li>build(deps-dev): bump prettier from 3.4.1 to 3.4.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3560">peter-evans/create-pull-request#3560</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 18.19.67 to
18.19.68 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3570">peter-evans/create-pull-request#3570</a></li>
<li>build(deps): bump p-limit from 6.1.0 to 6.2.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3578">peter-evans/create-pull-request#3578</a></li>
<li>Update distribution by <a
href="https://github.com/actions-bot"><code>@​actions-bot</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3583">peter-evans/create-pull-request#3583</a></li>
<li>fix: preserve unicode in filepaths when commit signing by <a
href="https://github.com/peter-evans"><code>@​peter-evans</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3588">peter-evans/create-pull-request#3588</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/benmccann"><code>@​benmccann</code></a>
made their first contribution in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3475">peter-evans/create-pull-request#3475</a></li>
<li><a href="https://github.com/scop"><code>@​scop</code></a> made their
first contribution in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3511">peter-evans/create-pull-request#3511</a></li>
<li><a href="https://github.com/caugner"><code>@​caugner</code></a> made
their first contribution in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3498">peter-evans/create-pull-request#3498</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="67ccf781d6"><code>67ccf78</code></a>
fix: preserve unicode in filepaths when commit signing (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3588">#3588</a>)</li>
<li><a
href="bb88e27d3f"><code>bb88e27</code></a>
build: update distribution (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3583">#3583</a>)</li>
<li><a
href="b378ed537a"><code>b378ed5</code></a>
build(deps): bump p-limit from 6.1.0 to 6.2.0 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3578">#3578</a>)</li>
<li><a
href="fa9200e5b4"><code>fa9200e</code></a>
build(deps-dev): bump <code>@​types/node</code> from 18.19.67 to
18.19.68 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3570">#3570</a>)</li>
<li><a
href="16e0059bfd"><code>16e0059</code></a>
build(deps-dev): bump prettier from 3.4.1 to 3.4.2 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3560">#3560</a>)</li>
<li><a
href="5bffd5ae80"><code>5bffd5a</code></a>
build(deps-dev): bump eslint-import-resolver-typescript (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3559">#3559</a>)</li>
<li><a
href="a22a0ddc21"><code>a22a0dd</code></a>
build(deps-dev): bump prettier from 3.4.0 to 3.4.1 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3544">#3544</a>)</li>
<li><a
href="b27ce378c8"><code>b27ce37</code></a>
build(deps-dev): bump <code>@​types/node</code> from 18.19.66 to
18.19.67 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3543">#3543</a>)</li>
<li><a
href="4e0cc19e22"><code>4e0cc19</code></a>
build(deps): bump <code>@​octokit/plugin-paginate-rest</code> from
11.3.5 to 11.3.6 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/3542">#3542</a>)</li>
<li><a
href="25b6871a4e"><code>25b6871</code></a>
docs: update scopes for push-to-fork</li>
<li>Additional commits viewable in <a
href="5e914681df...67ccf781d6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=peter-evans/create-pull-request&package-manager=github_actions&previous-version=7.0.5&new-version=7.0.6)](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-01-02 14:24:04 +00:00
reecebrowne
574c474804 Fix file drag and drop (#2575)
# Description

Drag and drop only loading first page.

Closes #(2560)

## Checklist

- [ X] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ X] I have performed a self-review of my own code
- [ X] I have attached images of the change if it is UI based
- [ X] I have commented my code, particularly in hard-to-understand
areas
- [ X] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ X] My changes generate no new warnings
- [ 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)
2024-12-31 10:44:49 +00:00
Ludy
2e23149090 extends the .gitignore for cert and ssh (#2572)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [x] My changes generate no new warnings
- [ ] 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)
2024-12-30 23:20:10 +00:00
Anthony Stirling
7be8db7832 Move docs to website (#2571)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2024-12-30 15:49:28 +00:00
Ludy
702433d4c5 removed actions/checkout for PR branch, use gh (#2567)
# Description

changes the permission
https://github.com/Stirling-Tools/Stirling-PDF/security/code-scanning/104

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2024-12-29 14:44:50 +00:00
Ludy
0e824005c3 add permissions for check_properties.yml (#2564)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2024-12-27 18:13:31 +00:00
github-actions[bot]
8c2a468915 📝 Update README: Translation Progress Table (#2565)
Auto-generated by [create-pull-request][1]

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

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-27 18:13:22 +00:00
Belhassen
7d81ca799f Adding the missing translations in French (#2563)
# Description

I have added the missing tags for the French translation

Closes #2315 

## Checklist

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2024-12-27 16:22:50 +00:00
Anthony Stirling
0f24fa104e Update push-docker only sigh fat and normal 2024-12-26 13:32:22 +00:00
Anthony Stirling
db1fa28230 Update build.yml (#2559)
# Description

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
2024-12-26 11:13:41 +00:00
113 changed files with 2213 additions and 1624 deletions

View File

@@ -49,12 +49,17 @@ Documentation:
Docker:
- changed-files:
- any-glob-to-any-file: '.github/workflows/build.yml'
- any-glob-to-any-file: '.github/workflows/push-docker.yml'
- any-glob-to-any-file: 'Dockerfile'
- any-glob-to-any-file: 'Dockerfile-*'
- any-glob-to-any-file: 'Dockerfile.*'
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
- any-glob-to-any-file: 'scripts/init.sh'
- any-glob-to-any-file: 'scripts/init-without-ocr.sh'
- any-glob-to-any-file: 'scripts/installFonts.sh'
- any-glob-to-any-file: 'test.sh'
- any-glob-to-any-file: 'test2.sh'
Test:
- changed-files:

View File

@@ -4,9 +4,15 @@ on:
issue_comment:
types: [created]
permissions:
contents: read
jobs:
check-comment:
runs-on: ubuntu-latest
permissions:
pull-requests: read
issues: read
if: |
github.event.issue.pull_request &&
(
@@ -68,6 +74,9 @@ jobs:
deploy-pr:
needs: check-comment
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
steps:
- name: Harden Runner
@@ -98,7 +107,9 @@ jobs:
- name: Get version number
id: versionNumber
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
run: |
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
- name: Login to Docker Hub
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0

View File

@@ -4,7 +4,8 @@ on:
pull_request:
types: [opened, synchronize, reopened, closed]
permissions: read-all
permissions:
contents: read
env:
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets

View File

@@ -3,7 +3,8 @@ on:
pull_request_target:
types: [opened, synchronize]
permissions: read-all
permissions:
contents: read
jobs:
labeler:

View File

@@ -6,13 +6,15 @@ on:
pull_request:
branches: ["main"]
permissions: read-all
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
permissions:
actions: read
security-events: write
strategy:
@@ -44,6 +46,17 @@ jobs:
run: ./gradlew clean build
env:
DOCKER_ENABLE_SECURITY: true
- name: Upload Test Reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports-jdk-${{ matrix.jdk-version }}
path: |
build/reports/tests/
build/test-results/
build/reports/problems/
retention-days: 3
docker-compose-tests:
# if: github.event_name == 'push' && github.ref == 'refs/heads/main' ||
@@ -82,7 +95,7 @@ jobs:
- name: Install Docker Compose
run: |
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- name: Set up Python

View File

@@ -6,12 +6,16 @@ on:
paths:
- "src/main/resources/messages_*.properties"
permissions: read-all
permissions:
contents: read # Allow read access to repository content
jobs:
check-files:
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
permissions:
issues: write # Allow posting comments on issues/PRs
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
@@ -20,26 +24,32 @@ jobs:
- name: Checkout main branch first
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: main
path: main-branch
fetch-depth: 0
- name: Checkout PR branch
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
path: pr-branch
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: "3.x"
- name: Install GitHub CLI
run: sudo apt-get update && sudo apt-get install -y gh
- name: Get PR data
id: get-pr-data
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const prNumber = context.payload.pull_request.number;
const repoOwner = context.payload.repository.owner.login;
const repoName = context.payload.repository.name;
const branch = context.payload.pull_request.head.ref;
console.log(`PR Number: ${prNumber}`);
console.log(`Repo Owner: ${repoOwner}`);
console.log(`Repo Name: ${repoName}`);
console.log(`Branch: ${branch}`);
core.setOutput("pr_number", prNumber);
core.setOutput("repo_owner", repoOwner);
core.setOutput("repo_name", repoName);
core.setOutput("branch", branch);
continue-on-error: true
- name: Fetch PR changed files
id: fetch-pr-changes
@@ -47,45 +57,125 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Fetching PR changed files..."
cd pr-branch
gh repo set-default ${{ github.repository }}
# Store files in a safe way, only allowing valid properties files
echo "Getting list of changed files from PR..."
gh pr view ${{ github.event.pull_request.number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > ../changed_files.txt
cd ..
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > changed_files.txt # Filter only matching property files
echo "Processing changed files..."
mapfile -t CHANGED_FILES < changed_files.txt
CHANGED_FILES_STR="${CHANGED_FILES[*]}"
echo "CHANGED_FILES=${CHANGED_FILES_STR}" >> $GITHUB_ENV
echo "Changed files: ${CHANGED_FILES_STR}"
- name: Determine reference file
- name: Determine reference file test
id: determine-file
run: |
echo "Determining reference file..."
if grep -Fxq "src/main/resources/messages_en_GB.properties" changed_files.txt; then
echo "Using PR branch reference file"
echo "REFERENCE_FILE=pr-branch/src/main/resources/messages_en_GB.properties" >> $GITHUB_ENV
else
echo "Using main branch reference file"
echo "REFERENCE_FILE=main-branch/src/main/resources/messages_en_GB.properties" >> $GITHUB_ENV
fi
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const fs = require("fs");
const path = require("path");
- name: Show REFERENCE_FILE
run: echo "Reference file is set to ${REFERENCE_FILE}"
const prNumber = ${{ steps.get-pr-data.outputs.pr_number }};
const repoOwner = "${{ steps.get-pr-data.outputs.repo_owner }}";
const repoName = "${{ steps.get-pr-data.outputs.repo_name }}";
const prRepoOwner = "${{ github.event.pull_request.head.repo.owner.login }}";
const prRepoName = "${{ github.event.pull_request.head.repo.name }}";
const branch = "${{ steps.get-pr-data.outputs.branch }}";
console.log(`Determining reference file for PR #${prNumber}`);
// Validate inputs
const validateInput = (input, regex, name) => {
if (!regex.test(input)) {
throw new Error(`Invalid ${name}: ${input}`);
}
};
validateInput(repoOwner, /^[a-zA-Z0-9_-]+$/, "repository owner");
validateInput(repoName, /^[a-zA-Z0-9._-]+$/, "repository name");
validateInput(branch, /^[a-zA-Z0-9._/-]+$/, "branch name");
// Get the list of changed files in the PR
const { data: files } = await github.rest.pulls.listFiles({
owner: repoOwner,
repo: repoName,
pull_number: prNumber,
});
// Filter for relevant files based on the PR changes
const changedFiles = files
.map(file => file.filename)
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]+\.properties$/.test(file));
console.log("Changed files:", changedFiles);
// Create a temporary directory for PR files
const tempDir = "pr-branch";
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
// Download and save each changed file
for (const file of changedFiles) {
const { data: fileContent } = await github.rest.repos.getContent({
owner: prRepoOwner,
repo: prRepoName,
path: file,
ref: branch,
});
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
const filePath = path.join(tempDir, file);
const dirPath = path.dirname(filePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
fs.writeFileSync(filePath, content);
console.log(`Saved file: ${filePath}`);
}
// Output the list of changed files for further processing
const fileList = changedFiles.join(" ");
core.exportVariable("FILES_LIST", fileList);
console.log("Files saved and listed in FILES_LIST.");
// Determine reference file
let referenceFilePath;
if (changedFiles.includes("src/main/resources/messages_en_GB.properties")) {
console.log("Using PR branch reference file.");
const { data: fileContent } = await github.rest.repos.getContent({
owner: prRepoOwner,
repo: prRepoName,
path: "src/main/resources/messages_en_GB.properties",
ref: branch,
});
referenceFilePath = "pr-branch-messages_en_GB.properties";
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
fs.writeFileSync(referenceFilePath, content);
} else {
console.log("Using main branch reference file.");
const { data: fileContent } = await github.rest.repos.getContent({
owner: repoOwner,
repo: repoName,
path: "src/main/resources/messages_en_GB.properties",
ref: "main",
});
referenceFilePath = "main-branch-messages_en_GB.properties";
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
fs.writeFileSync(referenceFilePath, content);
}
console.log(`Reference file path: ${referenceFilePath}`);
core.exportVariable("REFERENCE_FILE", referenceFilePath);
- name: Run Python script to check files
id: run-check
run: |
echo "Running Python script to check files..."
python main-branch/.github/scripts/check_language_properties.py \
python .github/scripts/check_language_properties.py \
--actor ${{ github.event.pull_request.user.login }} \
--reference-file "${REFERENCE_FILE}" \
--branch pr-branch \
--files "${CHANGED_FILES[@]}" > result.txt || true
--branch "pr-branch" \
--files "${FILES_LIST[@]}" > result.txt
continue-on-error: true # Continue the job even if this step fails
- name: Capture output
id: capture-output
@@ -98,7 +188,7 @@ jobs:
echo "EOF" >> $GITHUB_ENV
echo "${SCRIPT_OUTPUT}"
# Set FAIL_JOB to true if SCRIPT_OUTPUT contains ❌
# Determine job failure based on script output
if [[ "$SCRIPT_OUTPUT" == *"❌"* ]]; then
echo "FAIL_JOB=true" >> $GITHUB_ENV
else
@@ -117,18 +207,18 @@ jobs:
script: |
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
const prNumber = context.issue.number;
const issueNumber = context.issue.number;
// Find existing comment
const comments = await github.rest.issues.listComments({
owner: repoOwner,
repo: repoName,
issue_number: prNumber
issue_number: issueNumber
});
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
// Only allow the action user to update comments
// Only update or create comments by the action user
const expectedActor = "github-actions[bot]";
if (comment && comment.user.login === expectedActor) {
@@ -145,7 +235,7 @@ jobs:
await github.rest.issues.createComment({
owner: repoOwner,
repo: repoName,
issue_number: prNumber,
issue_number: issueNumber,
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
});
console.log("Created new comment.");

View File

@@ -7,7 +7,8 @@ on:
paths:
- "build.gradle"
permissions: read-all
permissions:
contents: read
jobs:
generate-license-report:
@@ -52,7 +53,7 @@ jobs:
- name: Create Pull Request
id: cpr
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Update 3rd Party Licenses"

View File

@@ -4,7 +4,8 @@ on:
schedule:
- cron: "30 20 * * *"
permissions: read-all
permissions:
contents: read
jobs:
labeler:

View File

@@ -5,7 +5,8 @@ on:
release:
types: [created]
permissions: read-all
permissions:
contents: read
jobs:
build-installers:
@@ -42,7 +43,7 @@ jobs:
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
with:
gradle-version: 8.7
gradle-version: 8.12
# Install Windows dependencies
- name: Install WiX Toolset

View File

@@ -4,7 +4,8 @@ on:
push:
branches: [main]
permissions: read-all
permissions:
contents: read
jobs:
update:
@@ -19,7 +20,7 @@ jobs:
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: 3.12
- name: Run Pre-Commit Hooks
@@ -35,7 +36,7 @@ jobs:
git diff --staged --quiet || git commit -m ":file_folder: pre-commit
> Made via .github/workflows/pre_commit.yml" || echo "pre-commit: no changes"
- name: Create Pull Request
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "ci: 🤖 format everything with pre-commit"

View File

@@ -9,14 +9,13 @@ on:
permissions:
contents: read
packages: write
id-token: write
jobs:
push:
runs-on: ubuntu-latest
permissions:
packages: write
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
@@ -33,7 +32,7 @@ jobs:
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
with:
gradle-version: 8.7
gradle-version: 8.12
- name: Run Gradle Command
run: ./gradlew clean build
@@ -41,7 +40,8 @@ jobs:
DOCKER_ENABLE_SECURITY: false
- name: Install cosign
uses: sigstore/cosign-installer@v3.7.0
if: github.ref == 'refs/heads/master'
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
with:
cosign-release: 'v2.4.1'
@@ -105,31 +105,18 @@ jobs:
sbom: true
- name: Sign regular images
if: github.ref == 'refs/heads/master'
env:
DIGEST: ${{ steps.build-push-regular.outputs.digest }}
TAGS: ${{ steps.meta.outputs.tags }}
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
run: |
# Always sign images regardless of branch
echo "$TAGS" | tr ',' '\n' | while read -r tag; do
cosign sign --yes \
--key env://COSIGN_PRIVATE_KEY \
"${tag}@${DIGEST}"
done
# For alpha builds specifically, we want to ensure they're marked as development builds
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "Signing alpha build with development attestation"
echo "$TAGS" | tr ',' '\n' | while read -r tag; do
if [[ $tag == *":alpha" ]]; then
cosign attest --key env://COSIGN_PRIVATE_KEY \
--predicate <(echo '{"type":"development"}') \
--yes "${tag}@${DIGEST}"
fi
done
fi
- name: Generate tags ultra-lite
id: meta2
@@ -162,18 +149,6 @@ jobs:
provenance: true
sbom: true
- name: Sign ultra-lite images
if: github.ref != 'refs/heads/main'
env:
DIGEST: ${{ steps.build-push-lite.outputs.digest }}
TAGS: ${{ steps.meta2.outputs.tags }}
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
run: |
echo "$TAGS" | tr ',' '\n' | while read -r tag; do
cosign sign --key env://COSIGN_PRIVATE_KEY --yes "${tag}@${DIGEST}"
done
- name: Generate tags fat
id: meta3
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
@@ -207,7 +182,7 @@ jobs:
sbom: true
- name: Sign fat images
if: github.ref != 'refs/heads/main'
if: github.ref == 'refs/heads/master'
env:
DIGEST: ${{ steps.build-push-fat.outputs.digest }}
TAGS: ${{ steps.meta3.outputs.tags }}

View File

@@ -5,7 +5,8 @@ on:
release:
types: [created]
permissions: read-all
permissions:
contents: read
jobs:
push:
@@ -37,7 +38,7 @@ jobs:
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
with:
gradle-version: 8.7
gradle-version: 8.12
- name: Generate jar (With Security=${{ matrix.enable_security }})
run: ./gradlew clean createExe

View File

@@ -5,7 +5,8 @@ on:
- cron: "30 0 * * *"
workflow_dispatch:
permissions: read-all
permissions:
contents: read
jobs:
stale:

View File

@@ -6,7 +6,8 @@ on:
branches:
- master
permissions: read-all
permissions:
contents: read
jobs:
push:

View File

@@ -9,7 +9,8 @@ on:
- "src/main/resources/messages_*.properties"
- "scripts/ignore_translation.toml"
permissions: read-all
permissions:
contents: read
jobs:
sync-readme:
@@ -42,7 +43,7 @@ jobs:
git diff --staged --quiet || git commit -m ":memo: Sync README
> Made via sync_files.yml" || echo "no changes"
- name: Create Pull Request
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update files

View File

@@ -6,7 +6,8 @@ on:
paths:
- "src/main/resources/messages_en_GB.properties"
permissions: read-all
permissions:
contents: read
jobs:
update-translations-main:
@@ -50,7 +51,7 @@ jobs:
- name: Create Pull Request
id: cpr
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Update translation files"

22
.gitignore vendored
View File

@@ -146,19 +146,37 @@ out/
# cucumber
/cucumber/reports/**
# Certs
# Certs and Security Files
*.p12
*.pk8
*.pem
*.crt
*.cer
*.cert
*.der
*.key
*.csr
*.kdbx
*.jks
*.asc
# SSH Keys
*.pub
*.priv
id_rsa
id_rsa.pub
id_ecdsa
id_ecdsa.pub
id_ed25519
id_ed25519.pub
.ssh/
*ssh
# cache
.cache
.ruff_cache
.mypy_cache
.pytest_cache
.ipynb_checkpoints
**/jcef-bundle/
**/jcef-bundle/

View File

@@ -35,10 +35,10 @@ Documentation for Stirling-PDF is handled in a separate repository. Please see [
First, make sure you've read the section [Pull Requests](#pull-requests).
To build from the source, please follow this [Guide](LocalRunGuide.md).
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)
## License
By contributing to this project, you agree that your contributions will be licensed under the [MIT License](LICENSE).

View File

@@ -1,5 +1,5 @@
# Build the application
FROM gradle:8.11-jdk17 AS build
FROM gradle:8.12-jdk17 AS build
# Set the working directory
WORKDIR /app

View File

@@ -1,46 +0,0 @@
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | qpdf | Java | Javascript | Unoconv | tesseract |
| ------------------- | ------- | ------- | -------- | ----- | --- | ------ | ------ | ----------- | -------- | ---- | ---------- | ------- | ----------- |
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ | | |
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | | | |
| crop | ✔️ | | | | | | | | | ✔️ | | | |
| extract-page | ✔️ | | | | | | | | | ✔️ | | | |
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | | | |
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | | | |
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ | | |
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | | | |
| remove-pages | ✔️ | | | | | | | | | ✔️ | | | |
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | | | |
| scale-pages | ✔️ | | | | | | | | | ✔️ | | | |
| split-pdfs | ✔️ | | | | | | | | | ✔️ | | | |
| file-to-pdf | | ✔️ | | | ✔️ | ✔️ | | ✔️ | | | | ✔️ | |
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | | | |
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | | | |
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | |
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | | | |
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| add-password | | | ✔️ | | | | | | | ✔️ | | | |
| add-watermark | | | ✔️ | | | | | | | ✔️ | | | |
| cert-sign | | | ✔️ | | | | | | | ✔️ | | | |
| remove-cert-sign | | | ✔️ | | | | | | | ✔️ | | | |
| change-permissions | | | ✔️ | | | | | | | ✔️ | | | |
| remove-password | | | ✔️ | | | | | | | ✔️ | | | |
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | | | |
| add-image | | | | ✔️ | | | | | | ✔️ | | | |
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | | | |
| auto-rename | | | | ✔️ | | | | | | ✔️ | | | |
| change-metadata | | | | ✔️ | | | | | | ✔️ | | | |
| compare | | | | ✔️ | | | | | | | ✔️ | | |
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | |
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
| extract-images | | | | ✔️ | | | | | | ✔️ | | | |
| flatten | | | | ✔️ | | | | | | | ✔️ | | |
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | | | |
| ocr-pdf | | | | ✔️ | ✔️ | | | | | | | | ✔ |
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
| repair | | | | ✔️ | ✔️ | | | ✔️ | ✔ | | | | |
| show-javascript | | | | ✔️ | | | | | | | ✔️ | | |
| sign | | | | ✔️ | | | | | | | ✔️ | | |

View File

@@ -1,41 +0,0 @@
## User Guide for Local Directory Scanning and File Processing
### Setting Up Watched Folders
- Create a folder where you want your files to be monitored. This is your 'watched folder'.
- The default directory for this is `./pipeline/watchedFolders/`.
- Place any directories you want to be scanned into this folder. This folder should contain multiple folders, each for their own tasks and pipelines.
### Configuring Processing with JSON Files
- In each directory you want processed (e.g., `./pipeline/watchedFolders/officePrinter`), include a JSON configuration file.
- This JSON file should specify how you want the files in the directory to be handled (e.g., what operations to perform on them). This can be made, configured, and downloaded from the Stirling-PDF Pipeline interface.
### Automatic Scanning and Processing
- The system automatically checks the watched folder every minute for new directories and files to process.
- When a directory with a valid JSON configuration file is found, it begins processing the files inside according to the configuration.
### Processing Steps
- Files in each directory are processed according to the instructions in the JSON file.
- This might involve file conversions, data filtering, renaming files, etc. If the output of a step is a zip, this zip will be automatically unzipped as it passes to the next process.
### Results and Output
- After processing, the results are saved in a specified output location. This could be a different folder or location as defined in the JSON file or the default location `./pipeline/finishedFolders/`.
- Each processed file is named and organized according to the rules set in the JSON configuration.
### Completion and Cleanup
- Once processing is complete, the original files in the watched folder's directory are removed.
- You can find the processed files in the designated output location.
### Error Handling
- If there's an error during processing, the system will not delete the original files, allowing you to check and retry if necessary.
### User Interaction
- As a user, your main tasks are to set up the watched folders, place directories with files for processing, and create the corresponding JSON configuration files.
- The system handles the rest, including scanning, processing, and outputting results.

View File

@@ -1,327 +0,0 @@
To run the application without Docker/Podman, you will need to manually install all dependencies and build the necessary components.
Note that some dependencies might not be available in the standard repositories of all Linux distributions, and may require additional steps to install.
The following guide assumes you have a basic understanding of using a command line interface in your operating system.
It should work on most Linux distributions and MacOS. For Windows, you might need to use Windows Subsystem for Linux (WSL) for certain steps. The amount of dependencies is to actually reduce overall size, i.e., installing LibreOffice subcomponents rather than the full LibreOffice package.
You could theoretically use a Distrobox/Toolbox if your distribution has old or not all packages. But you might just as well use the Docker container then.
### Step 1: Prerequisites
Install the following software, if not already installed:
- Java 17 or later (21 recommended)
- Gradle 7.0 or later (included within the repo, so not needed on the server)
- Git
- Python 3.8 (with pip)
- Make
- GCC/G++
- Automake
- Autoconf
- libtool
- pkg-config
- zlib1g-dev
- libleptonica-dev
For Debian-based systems, you can use the following command:
```bash
sudo apt-get update
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ openjdk-21-jdk python3 python3-pip
```
For Fedora-based systems, use this command:
```bash
sudo dnf install -y git automake autoconf libtool leptonica-devel pkg-config zlib-devel make gcc-c++ java-21-openjdk python3 python3-pip
```
For non-root users with Nix Package Manager, use the following command:
```bash
nix-channel --update
nix-env -iA nixpkgs.jdk21 nixpkgs.git nixpkgs.python38 nixpkgs.gnumake nixpkgs.libgcc nixpkgs.automake nixpkgs.autoconf nixpkgs.libtool nixpkgs.pkg-config nixpkgs.zlib nixpkgs.leptonica
```
### Step 2: Clone and Build jbig2enc (Only required for certain OCR functionality)
For Debian and Fedora, you can build it from source using the following commands:
```bash
mkdir ~/.git
cd ~/.git && \
git clone https://github.com/agl/jbig2enc.git && \
cd jbig2enc && \
./autogen.sh && \
./configure && \
make && \
sudo make install
```
For Nix, you will face `Leptonica not detected`. Bypass this by installing it directly using the following command:
```bash
nix-env -iA nixpkgs.jbig2enc
```
### Step 3: Install Additional Software
Next, we need to install LibreOffice for conversions, qpdf for OCR, and OpenCV for pattern recognition functionality.
Install the following software:
- libreoffice-core
- libreoffice-common
- libreoffice-writer
- libreoffice-calc
- libreoffice-impress
- python3-uno
- unoconv
- pngquant
- unpaper
- qpdf
- opencv-python-headless
For Debian-based systems, you can use the following command:
```bash
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages
```
For Fedora:
```bash
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
```
For Nix:
```bash
nix-env -iA nixpkgs.unpaper nixpkgs.libreoffice nixpkgs.qpdf nixpkgs.poppler_utils
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
```
### Step 4: Clone and Build Stirling-PDF
```bash
cd ~/.git && \
git clone https://github.com/Stirling-Tools/Stirling-PDF.git && \
cd Stirling-PDF && \
chmod +x ./gradlew && \
./gradlew build
```
### Step 5: Move Jar to Desired Location
After the build process, a `.jar` file will be generated in the `build/libs` directory. You can move this file to a desired location, for example, `/opt/Stirling-PDF/`. You must also move the Script folder within the Stirling-PDF repo that you have downloaded to this directory. This folder is required for the Python scripts using OpenCV.
```bash
sudo mkdir /opt/Stirling-PDF && \
sudo mv ./build/libs/Stirling-PDF-*.jar /opt/Stirling-PDF/ && \
sudo mv scripts /opt/Stirling-PDF/ && \
echo "Scripts installed."
```
For non-root users, you can just keep the jar in the main directory of Stirling-PDF using the following command:
```bash
mv ./build/libs/Stirling-PDF-*.jar ./Stirling-PDF-*.jar
```
### Step 6: Other Files
#### OCR
If you plan to use the OCR (Optical Character Recognition) functionality, you might need to install language packs for Tesseract if running non-English scanning.
##### Installing Language Packs
The easiest method is to use the language packs provided by your repositories. Skip the other steps if they are available.
**Manual:**
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
**Debian-based systems**, install languages with this command:
```bash
sudo apt update && \
# All languages
# sudo apt install -y 'tesseract-ocr-*'
# Find languages:
apt search tesseract-ocr-
# View installed languages:
dpkg-query -W tesseract-ocr- | sed 's/tesseract-ocr-//g'
```
**Fedora:**
```bash
# All languages
# sudo dnf install -y tesseract-langpack-*
# Find languages:
dnf search -C tesseract-langpack-
# View installed languages:
rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'
```
**Nix:**
```bash
nix-env -iA nixpkgs.tesseract
```
**Note:** Nix Package Manager pre-installs almost all the language packs when Tesseract is installed.
### Step 7: Run Stirling-PDF
Those who have pushed to the root directory, run the following commands:
```bash
./gradlew bootRun
or
java -jar /opt/Stirling-PDF/Stirling-PDF-*.jar
```
Since LibreOffice, soffice, and conversion tools have their dbus_tmp_dir set as `dbus_tmp_dir="/run/user/$(id -u)/libreoffice-dbus"`, you might get the following error when using their endpoints:
```
[Thread-7] INFO s.s.SPDF.utils.ProcessExecutor - mkdir: cannot create directory /run/user/1501: Permission denied
```
To resolve this, before starting Stirling-PDF, you have to set the environment variable to a directory you have write access to by using the following commands:
```bash
mkdir temp
export DBUS_SESSION_BUS_ADDRESS="unix:path=./temp"
./gradlew bootRun
or
java -jar ./Stirling-PDF-*.jar
```
### Step 8: Adding a Desktop Icon
This will add a modified app starter to your app menu.
```bash
location=$(pwd)/gradlew
image=$(pwd)/docs/stirling-transparent.svg
cat > ~/.local/share/applications/Stirling-PDF.desktop <<EOF
[Desktop Entry]
Name=Stirling PDF;
GenericName=Launch StirlingPDF and open its WebGUI;
Category=Office;
Exec=xdg-open http://localhost:8080 && nohup $location bootRun &;
Icon=$image;
Keywords=pdf;
Type=Application;
NoDisplay=false;
Terminal=true;
EOF
```
Note: Currently, the app will run in the background until it is manually closed.
### Optional: Changing the Host and Port of the Application
To override the default configuration, you can add the following to `/.git/Stirling-PDF/configs/custom_settings.yml` file:
```yaml
server:
host: 0.0.0.0 # Not working - use instead address
address: 0.0.0.0
port: 3000
```
`-Djava.net.preferIPv4Stack=true` --> To force IPv4 only in the Java starting command
**Note:** This file is created after the first application launch. To have it before that, you can create the directory and add the file yourself.
### Optional: Run Stirling-PDF as a Service (requires root)
First, create a `.env` file, where you can store environment variables:
```bash
touch /opt/Stirling-PDF/.env
```
In this file, you can add all variables, one variable per line, as stated in the main readme (for example `SYSTEM_DEFAULTLOCALE="de-DE"`).
Create a new file where we store our service settings and open it with the nano editor:
```bash
nano /etc/systemd/system/stirlingpdf.service
```
Paste this content, and make sure to update the filename of the jar file. Press `Ctrl+S` and `Ctrl+X` to save and exit the nano editor:
```ini
[Unit]
Description=Stirling-PDF service
After=syslog.target network.target
[Service]
SuccessExitStatus=143
User=root
Group=root
Type=simple
EnvironmentFile=/opt/Stirling-PDF/.env
WorkingDirectory=/opt/Stirling-PDF
ExecStart=/usr/bin/java -jar Stirling-PDF-0.17.2.jar
ExecStop=/bin/kill -15 $MAINPID
[Install]
WantedBy=multi-user.target
```
Notify systemd that it has to rebuild its internal service database (you have to run this command every time you make a change in the service file):
```bash
sudo systemctl daemon-reload
```
Enable the service to tell it to start automatically:
```bash
sudo systemctl enable stirlingpdf.service
```
See the status of the service:
```bash
sudo systemctl status stirlingpdf.service
```
Manually start/stop/restart the service:
```bash
sudo systemctl start stirlingpdf.service
sudo systemctl stop stirlingpdf.service
sudo systemctl restart stirlingpdf.service
```
---
Remember to set the necessary environment variables before running the project if you want to customize the application. The list can be seen in the main readme.
You can do this in the terminal by using the `export` command or `-D` argument to the Java `-jar` command:
```bash
export APP_HOME_NAME="Stirling PDF"
or
-DAPP_HOME_NAME="Stirling PDF"

View File

@@ -1,42 +0,0 @@
# Pipeline Configuration and Usage Tutorial
- Configure the pipeline config file and input files to run files against it.
- For reuse, download the config file and re-upload it when needed, or place it in `/pipeline/defaultWebUIConfigs/` to auto-load in the web UI for all users.
## Steps to Configure and Use Your Pipeline
1. **Access Configuration**
- Upon entering the screen, click on the **Configure** button.
2. **Enter Pipeline Name**
- Provide a name for your pipeline in the designated field.
3. **Select Operations**
- Choose the operations for your pipeline (e.g., **Split Pages**), then click **Add Operation**.
4. **Configure Operation Settings**
- Input the necessary settings for each added operation. Settings are highlighted in yellow if customization is needed.
5. **Add More Operations**
- You can add and adjust the order of multiple operations. Ensure each operation's settings are customized.
6. **Save Settings**
- Click **Save Operation Settings** after customizing settings for each operation.
7. **Validate Pipeline**
- Use the **Validation** button to check your pipeline. A green indicator signifies correct setup; a pop-out error indicates issues.
8. **Download Pipeline Configuration**
- To use the configuration for folder scanning (or save it for future use and re-upload it), download a JSON file in this menu. You can also pre-load it for future use by placing it in `/pipeline/defaultWebUIConfigs/`. It will then appear in the dropdown menu for all users to use.
9. **Submit Files for Processing**
- If your pipeline is correctly set up, close the configure menu, input the files, and hit **Submit**.
10. **Note on Web UI Limitations**
- The current web UI version does not support operations that require multiple different types of inputs, such as adding a separate image to a PDF.
### Current Limitations
- Cannot have more than one of the same operation.
- Cannot input additional files via UI.
- All files and operations run in serial mode.

327
README.md
View File

@@ -4,6 +4,7 @@
[![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf)
[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/HYmhKj45pU)
[![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Stirling-Tools/Stirling-PDF/)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Stirling-Tools/Stirling-PDF/badge)](https://scorecard.dev/viewer/?uri=github.com/Stirling-Tools/Stirling-PDF)
[![GitHub Repo stars](https://img.shields.io/github/stars/stirling-tools/stirling-pdf?style=social)](https://github.com/Stirling-Tools/stirling-pdf)
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling&#0045;pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling&#0032;PDF - Open&#0032;source&#0032;locally&#0032;hosted&#0032;web&#0032;PDF&#0032;editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
@@ -13,18 +14,22 @@
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
All information available at [https://docs.stirlingpdf.com/](https://docs.stirlingpdf.com/)
![stirling-home](images/stirling-home.jpg)
## Features
- Enterprise features like SSO Check [here](https://docs.stirlingpdf.com/Enterprise%20Edition)
- 50+ PDF Operations
- Parallel file processing and downloads
- Dark mode support
- Custom download options
- Parallel file processing and downloads
- Custom 'Pipelines' to run multiple features in a queue
- Custom 'Pipelines' to run multiple features in a automated queue
- API for integration with external scripts
- Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation)
- Database Backup and Import (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DATABASE.md) for documentation)
- 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)
## PDF Features
@@ -90,97 +95,20 @@ All files and PDFs exist either exclusively on the client side, reside in server
- Get all information on a PDF to view or export as JSON
- Show/detect embedded JavaScript
For an overview of the tasks and the technology each uses, please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md).
A demo of the app is available [here](https://stirlingpdf.io).
## Technologies Used
- Spring Boot + Thymeleaf
- [PDFBox](https://github.com/apache/pdfbox/tree/trunk)
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
- [qpdf](https://github.com/qpdf/qpdf)
- HTML, CSS, JavaScript
- Docker
- [PDF.js](https://github.com/mozilla/pdf.js)
- [PDF-LIB.js](https://github.com/Hopding/pdf-lib)
# 📖 Get Started
## How to Use
Visit our comprehensive documentation at [docs.stirlingpdf.com](https://docs.stirlingpdf.com) for:
### Windows
- Installation guides for all platforms
- Configuration options
- Feature documentation
- API reference
- Security setup
- Enterprise features
For Windows users, download the latest Stirling-PDF.exe from our [release](https://github.com/Stirling-Tools/Stirling-PDF/releases) section or by clicking [here](https://github.com/Stirling-Tools/Stirling-PDF/releases/latest/download/Stirling-PDF.exe).
### Locally
Please view the [LocalRunGuide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/LocalRunGuide.md).
### Docker / Podman
> [!NOTE]
> <https://hub.docker.com/r/stirlingtools/stirling-pdf>
Stirling-PDF has three different versions: a full version, an ultra-lite version, and a 'fat' version. Depending on the types of features you use, you may want a smaller image to save on space. To see what the different versions offer, please look at our [version mapping](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Version-groups.md). For people who don't mind space optimization, just use the latest tag.
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/stirlingtools/stirling-pdf/latest?label=Stirling-PDF%20Full)
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/stirlingtools/stirling-pdf/latest-ultra-lite?label=Stirling-PDF%20Ultra-Lite)
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/stirlingtools/stirling-pdf/latest-fat?label=Stirling-PDF%20Fat)
Please note in the examples below, you may need to change the volume paths as needed, e.g., `./extraConfigs:/configs` to `/opt/stirlingpdf/extraConfigs:/configs`.
### Docker Run
```bash
docker run -d \
-p 8080:8080 \
-v ./trainingData:/usr/share/tessdata \
-v ./extraConfigs:/configs \
-v ./logs:/logs \
# Optional customization (not required)
# -v /location/of/customFiles:/customFiles \
-e DOCKER_ENABLE_SECURITY=false \
-e INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
-e LANGS=en_GB \
--name stirling-pdf \
stirlingtools/stirling-pdf:latest
```
### Docker Compose
```yaml
version: '3.3'
services:
stirling-pdf:
image: stirlingtools/stirling-pdf:latest
ports:
- '8080:8080'
volumes:
- ./trainingData:/usr/share/tessdata # Required for extra OCR languages
- ./extraConfigs:/configs
# - ./customFiles:/customFiles/
# - ./logs:/logs/
environment:
- DOCKER_ENABLE_SECURITY=false
- INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
- LANGS=en_GB
```
Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "podman".
### Kubernetes
See the kubernetes helm chart [here](https://github.com/Stirling-Tools/Stirling-PDF-chart)
## Enable OCR/Compression Feature
Please view the [HowToUseOCR.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md).
## Reuse Stored Files
Certain functionality like `Sign` supports pre-saved files stored at `/customFiles/signatures/`. Image files placed here will be accessible via the web UI. Currently, this supports two folder types:
- `/customFiles/signatures/ALL_USERS`: Accessible to all users, useful for organizations where many users use the same files or for users not using authentication
- `/customFiles/signatures/{username}`: Such as `/customFiles/signatures/froodle`, accessible only to the `froodle` username, private for all others
## Supported Languages
@@ -188,236 +116,57 @@ Stirling-PDF currently supports 38 languages!
| Language | Progress |
| -------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![94%](https://geps.dev/progress/94) |
| Arabic (العربية) (ar_AR) | ![93%](https://geps.dev/progress/93) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![92%](https://geps.dev/progress/92) |
| Basque (Euskara) (eu_ES) | ![53%](https://geps.dev/progress/53) |
| Bulgarian (Български) (bg_BG) | ![89%](https://geps.dev/progress/89) |
| Catalan (Català) (ca_CA) | ![84%](https://geps.dev/progress/84) |
| Croatian (Hrvatski) (hr_HR) | ![91%](https://geps.dev/progress/91) |
| Croatian (Hrvatski) (hr_HR) | ![90%](https://geps.dev/progress/90) |
| Czech (Česky) (cs_CZ) | ![90%](https://geps.dev/progress/90) |
| Danish (Dansk) (da_DK) | ![89%](https://geps.dev/progress/89) |
| Dutch (Nederlands) (nl_NL) | ![89%](https://geps.dev/progress/89) |
| 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) | ![92%](https://geps.dev/progress/92) |
| German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![96%](https://geps.dev/progress/96) |
| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) |
| Greek (Ελληνικά) (el_GR) | ![90%](https://geps.dev/progress/90) |
| Hindi (हिंदी) (hi_IN) | ![88%](https://geps.dev/progress/88) |
| Hungarian (Magyar) (hu_HU) | ![91%](https://geps.dev/progress/91) |
| Hungarian (Magyar) (hu_HU) | ![90%](https://geps.dev/progress/90) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![90%](https://geps.dev/progress/90) |
| Irish (Gaeilge) (ga_IE) | ![82%](https://geps.dev/progress/82) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![93%](https://geps.dev/progress/93) |
| Korean (한국어) (ko_KR) | ![89%](https://geps.dev/progress/89) |
| Norwegian (Norsk) (no_NB) | ![82%](https://geps.dev/progress/82) |
| Persian (فارسی) (fa_IR) | ![99%](https://geps.dev/progress/99) |
| Polish (Polski) (pl_PL) | ![90%](https://geps.dev/progress/90) |
| Persian (فارسی) (fa_IR) | ![98%](https://geps.dev/progress/98) |
| Polish (Polski) (pl_PL) | ![89%](https://geps.dev/progress/89) |
| Portuguese (Português) (pt_PT) | ![90%](https://geps.dev/progress/90) |
| Portuguese Brazilian (Português) (pt_BR) | ![98%](https://geps.dev/progress/98) |
| Romanian (Română) (ro_RO) | ![84%](https://geps.dev/progress/84) |
| Russian (Русский) (ru_RU) | ![90%](https://geps.dev/progress/90) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![67%](https://geps.dev/progress/67) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![66%](https://geps.dev/progress/66) |
| Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) |
| Slovakian (Slovensky) (sk_SK) | ![78%](https://geps.dev/progress/78) |
| Spanish (Español) (es_ES) | ![91%](https://geps.dev/progress/91) |
| Slovakian (Slovensky) (sk_SK) | ![77%](https://geps.dev/progress/77) |
| Spanish (Español) (es_ES) | ![90%](https://geps.dev/progress/90) |
| Swedish (Svenska) (sv_SE) | ![90%](https://geps.dev/progress/90) |
| Thai (ไทย) (th_TH) | ![90%](https://geps.dev/progress/90) |
| Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) |
| Turkish (Türkçe) (tr_TR) | ![86%](https://geps.dev/progress/86) |
| Ukrainian (Українська) (uk_UA) | ![76%](https://geps.dev/progress/76) |
| Ukrainian (Українська) (uk_UA) | ![75%](https://geps.dev/progress/75) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![83%](https://geps.dev/progress/83) |
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
Please see our [Contributing Guide](CONTRIBUTING.md).
## Stirling PDF Enterprise
Stirling PDF offers an Enterprise edition of its software. This is the same great software but with added features and comforts.
Stirling PDF offers an Enterprise edition of its software. This is the same great software but with added features, support and comforts.
Check out our [Enterprise docs](https://docs.stirlingpdf.com/Enterprise%20Edition)
### What's included
- Prioritized Support tickets via support@stirlingpdf.com to reach directly to Stirling-PDF team for support and 1:1 meetings where applicable (Provided they come from the same email domain registered with us)
- Prioritised Enhancements to Stirling-PDF where applicable
- Base SSO support
- Advanced SSO such as automated login handling (Coming very soon)
- SAML SSO (Coming very soon)
- Custom automated metadata handling
- Advanced user configurations (Coming soon)
- Plus other exciting features to come
## 🤝 Looking to contribute?
Check out our [docs](https://docs.stirlingpdf.com/Enterprise%20Edition) on it or our official [website](https://www.stirlingpdf.com)
## Customization
Stirling-PDF allows easy customization of the app, including things like:
- Custom application name
- Custom slogans, icons, HTML, images, CSS, etc. (via file overrides)
There are two options for this, either using the generated settings file `settings.yml`, which is located in the `/configs` directory and follows standard YAML formatting, or using environment variables, which would override the settings file.
For example, in `settings.yml`, you might have:
```yaml
security:
enableLogin: 'true'
```
To have this via an environment variable, you would use `SECURITY_ENABLELOGIN`.
The current list of settings is:
```yaml
security:
enableLogin: false # set to 'true' to enable login
csrfDisabled: true # set to 'true' to disable CSRF protection (not recommended for production)
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2)
initialLogin:
username: '' # initial username for the first login
password: '' # initial password for the first login
oauth2:
enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
client:
keycloak:
issuer: '' # URL of the Keycloak realm's OpenID Connect Discovery endpoint
clientId: '' # client ID for Keycloak OAuth2
clientSecret: '' # client secret for Keycloak OAuth2
scopes: openid, profile, email # scopes for Keycloak OAuth2
useAsUsername: preferred_username # field to use as the username for Keycloak OAuth2
google:
clientId: '' # client ID for Google OAuth2
clientSecret: '' # client secret for Google OAuth2
scopes: https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile # scopes for Google OAuth2
useAsUsername: email # field to use as the username for Google OAuth2
github:
clientId: '' # client ID for GitHub OAuth2
clientSecret: '' # client secret for GitHub OAuth2
scopes: read:user # scope for GitHub OAuth2
useAsUsername: login # field to use as the username for GitHub OAuth2
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
clientId: '' # client ID from your provider
clientSecret: '' # client secret from your provider
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
useAsUsername: email # default is 'email'; custom fields can be used as the username
scopes: openid, profile, email # specify the scopes for which the application will request permissions
provider: google # set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
saml2:
enabled: false # currently in alpha, not recommended for use yet, enableAlphaFunctionality must be set to true
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
registrationId: stirling
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata
idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml
idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml
idpIssuer: http://www.okta.com/externalKey
idpCert: classpath:okta.crt
privateKey: classpath:saml-private-key.key
spCert: classpath:saml-public-cert.crt
enterpriseEdition:
enabled: false # set to 'true' to enable enterprise edition
key: 00000000-0000-0000-0000-000000000000
CustomMetadata:
autoUpdateMetadata: false # set to 'true' to automatically update metadata with below values
author: username # supports text such as 'John Doe' or types such as username to autopopulate with user's username
creator: Stirling-PDF # supports text such as 'Company-PDF'
producer: Stirling-PDF # supports text such as 'Company-PDF'
legal:
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
impressum: '' # URL to the impressum of your application (e.g. https://example.com/impressum). Empty string to disable or filename to load from local file in static folder
system:
defaultLocale: en-US # set the default language (e.g. 'de-DE', 'fr-FR', etc)
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow
enableAlphaFunctionality: false # set to enable functionality which might need more testing before it fully goes live (this feature might make no changes)
showUpdate: false # see when a new update is available
showUpdateOnlyAdmin: false # only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
enableAnalytics: undefined # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
ui:
appName: '' # application's visible name
homeDescription: '' # short description or tagline shown on the homepage
appNameNavbar: '' # name displayed on the navigation bar
endpoints:
toRemove: [] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])
groupsToRemove: [] # list groups to disable (e.g. ['LibreOffice'])
metrics:
enabled: true # 'true' to enable Info APIs (`/api/*`) endpoints, 'false' to disable
# Automatically Generated Settings (Do Not Edit Directly)
AutomaticallyGenerated:
key: example
UUID: example
```
There is an additional config file `/configs/custom_settings.yml` where users familiar with Java and Spring `application.properties` can input their own settings on top of Stirling-PDF's existing ones.
### Extra Notes
- **Endpoints**: Currently, the `ENDPOINTS_TO_REMOVE` and `GROUPS_TO_REMOVE` endpoints can include comma-separated lists of endpoints and groups to disable. For example, `ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages` would disable both image-to-pdf and remove pages, while `GROUPS_TO_REMOVE=LibreOffice` would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md).
- **customStaticFilePath**: Customize static files such as the app logo by placing files in the `/customFiles/static/` directory. An example of customizing the app logo is placing `/customFiles/static/favicon.svg` to override the current SVG. This can be used to change any `images/icons/css/fonts/js`, etc. in Stirling-PDF.
### Environment-Only Parameters
- `SYSTEM_ROOTURIPATH` - Set the application's root URI (e.g. `/pdf-app` to set the root URI to `localhost:8080/pdf-app`)
- `SYSTEM_CONNECTIONTIMEOUTMINUTES` - Set custom connection timeout values
- `DOCKER_ENABLE_SECURITY` - Set to `true` to download security jar (required for authentication login)
- `INSTALL_BOOK_AND_ADVANCED_HTML_OPS` - Download Calibre onto Stirling-PDF to enable PDF to/from book and advanced HTML conversion
- `LANGS` - Define custom font libraries to install for document conversions
## API
For those wanting to use Stirling-PDF's backend API to link with their own custom scripting to edit PDFs, you can view all existing API documentation [here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/), or navigate to `/swagger-ui/index.html` of your Stirling-PDF instance for your version's documentation (or by following the API button in the settings of Stirling-PDF).
## Login Authentication
![stirling-login](images/login-light.png)
### Prerequisites
- User must have the folder `./configs` volumed within Docker so that it is retained during updates.
- Docker users must download the security jar version by setting `DOCKER_ENABLE_SECURITY` to `true` in environment variables.
- Then either enable login via the `settings.yml` file or set `SECURITY_ENABLE_LOGIN` to `true`.
- Now the initial user will be generated with username `admin` and password `stirling`. On login, you will be forced to change the password to a new one. You can also use the environment variables `SECURITY_INITIALLOGIN_USERNAME` and `SECURITY_INITIALLOGIN_PASSWORD` to set your own credentials straight away (recommended to remove them after user creation).
Once the above has been done, on restart, a new `stirling-pdf-DB.mv.db` will show if everything worked.
When you log in to Stirling-PDF, you will be redirected to the `/login` page to log in with those default credentials. After login, everything should function as normal.
To access your account settings, go to Account Settings in the settings cog menu (top right in the navbar). This Account Settings menu is also where you find your API key.
To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here, you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future.
For API usage, you must provide a header with `X-API-KEY` and the associated API key for that user.
## FAQ
### Q1: What are your planned features?
- Progress bar/tracking
- Full custom logic pipelines to combine multiple operations together
- Folder support with auto-scanning to perform operations on
- Redact text (via UI, not just automated)
- Add forms
- Multi-page layout (stitch PDF pages together) support x rows y columns and custom page sizing
- Fill forms manually or automatically
### Q2: Why is my application downloading .htm files? Why am I getting HTTP error 413?
This is an issue commonly caused by your NGINX configuration. The default file upload size for NGINX is 1MB. You need to add the following in your Nginx sites-available file: `client_max_body_size SIZE;` (where "SIZE" is 50M, for example, for 50MB files).
### Q3: Why is my download timing out?
NGINX has timeout values by default, so if you are running Stirling-PDF behind NGINX, you may need to set a timeout value, such as adding the config `proxy_read_timeout 3600;`.
Join our community:
- [Contribution Guidelines](CONTRIBUTING.md)
- [Translation Guide (How to add custom languages)](HowToAddNewLanguage.md)
- [Issue Tracker](https://github.com/Stirling-Tools/Stirling-PDF/issues)
- [Discord Community](https://discord.gg/HYmhKj45pU)
- [Developer Guide](DeveloperGuide.md)

View File

@@ -1,68 +0,0 @@
All versions in a Docker environment can download Calibre as a optional extra at runtime to support `book-to-pdf` and `pdf-to-book` using parameter ``INSTALL_BOOK_AND_ADVANCED_HTML_OPS``.
The 'Fat' container contains all those found in 'Full' with security jar along with this Calibre install.
| Technology | Ultra-Lite | Full |
| ---------- | :--------: | :---: |
| Java | ✔️ | ✔️ |
| JavaScript | ✔️ | ✔️ |
| Libre | | ✔️ |
| Python | | ✔️ |
| OpenCV | | ✔️ |
| qpdf | | ✔️ |
| Operation | Ultra-Lite | Full |
| ---------------------- | ---------- | ---- |
| add-page-numbers | ✔️ | ✔️ |
| add-password | ✔️ | ✔️ |
| add-image | ✔️ | ✔️ |
| add-watermark | ✔️ | ✔️ |
| adjust-contrast | ✔️ | ✔️ |
| auto-split-pdf | ✔️ | ✔️ |
| auto-redact | ✔️ | ✔️ |
| auto-rename | ✔️ | ✔️ |
| cert-sign | ✔️ | ✔️ |
| remove-cert-sign | ✔️ | ✔️ |
| crop | ✔️ | ✔️ |
| change-metadata | ✔️ | ✔️ |
| change-permissions | ✔️ | ✔️ |
| compare | ✔️ | ✔️ |
| extract-page | ✔️ | ✔️ |
| extract-images | ✔️ | ✔️ |
| flatten | ✔️ | ✔️ |
| get-info-on-pdf | ✔️ | ✔️ |
| img-to-pdf | ✔️ | ✔️ |
| markdown-to-pdf | ✔️ | ✔️ |
| merge-pdfs | ✔️ | ✔️ |
| multi-page-layout | ✔️ | ✔️ |
| overlay-pdf | ✔️ | ✔️ |
| pdf-organizer | ✔️ | ✔️ |
| pdf-to-csv | ✔️ | ✔️ |
| pdf-to-img | ✔️ | ✔️ |
| pdf-to-single-page | ✔️ | ✔️ |
| remove-pages | ✔️ | ✔️ |
| remove-password | ✔️ | ✔️ |
| rotate-pdf | ✔️ | ✔️ |
| sanitize-pdf | ✔️ | ✔️ |
| scale-pages | ✔️ | ✔️ |
| sign | ✔️ | ✔️ |
| show-javascript | ✔️ | ✔️ |
| split-by-size-or-count | ✔️ | ✔️ |
| split-pdf-by-sections | ✔️ | ✔️ |
| split-pdfs | ✔️ | ✔️ |
| compress-pdf | | ✔️ |
| extract-image-scans | | ✔️ |
| ocr-pdf | | ✔️ |
| pdf-to-pdfa | | ✔️ |
| remove-blanks | | ✔️ |
| pdf-to-text | ✔️ | ✔️ |
| pdf-to-html | | ✔️ |
| pdf-to-word | | ✔️ |
| pdf-to-presentation | | ✔️ |
| pdf-to-xml | | ✔️ |
| remove-annotations | ✔️ | ✔️ |
| remove-cert-sign | ✔️ | ✔️ |
| remove-image-pdf | ✔️ | ✔️ |
| file-to-pdf | | ✔️ |
| html-to-pdf | | ✔️ |
| url-to-pdf | | ✔️ |
| repair | | ✔️ |

View File

@@ -27,7 +27,7 @@ ext {
}
group = "stirling.software"
version = "0.36.5"
version = "0.36.6"
java {
@@ -123,11 +123,13 @@ jpackage {
windows {
launcherAsService = false
appVersion = project.version
winConsole = false
winDirChooser = true
winMenu = true
winShortcut = true
winPerUserInstall = true
winConsole = false
winMenu = true // Creates start menu entry
winShortcut = true // Creates desktop shortcut
winShortcutPrompt = true // Lets user choose whether to create shortcuts
winDirChooser = true // Allows users to choose installation directory
winPerUserInstall = false
winMenuGroup = "Stirling Software"
winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates
winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF"
@@ -257,7 +259,7 @@ spotless {
// rules=['unused-dependency']
// }
tasks.wrapper {
gradleVersion = "8.7"
gradleVersion = "8.12"
}
//tasks.withType(JavaCompile) {
// options.compilerArgs << "-Xlint:deprecation"

View File

@@ -1,7 +1,7 @@
@general
Feature: API Validation
@split-pdf-by-sections @positive
Scenario Outline: split-pdf-by-sections with different parameters
Given I generate a PDF file as "fileInput"
@@ -66,7 +66,7 @@ Feature: API Validation
| pageNumbers | file_count |
| 1,3,5-9 | 8 |
| all | 20 |
| 2n+1 | 11 |
| 2n+1 | 10 |
| 3n | 7 |
@@ -106,9 +106,9 @@ Feature: API Validation
And the response ZIP should contain 2 files
And the response file should have size greater than 0
And the response status code should be 200
Examples:
| format |
| png |
| format |
| png |
| gif |
| jpeg |
| jpeg |

View File

@@ -2,6 +2,7 @@ package stirling.software.SPDF;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -24,6 +25,7 @@ import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.SPDF.config.ConfigInitializer;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.ApplicationProperties;
@SpringBootApplication
@@ -76,15 +78,27 @@ public class SPdfApplication {
props.put("spring.main.web-application-type", "servlet");
}
app.setAdditionalProfiles("default");
app.addInitializers(new ConfigInitializer());
ConfigInitializer initializer = new ConfigInitializer();
try {
initializer.ensureConfigExists();
} catch (IOException | URISyntaxException e) {
log.error("Error initialising configuration", e);
}
Map<String, String> propertyFiles = new HashMap<>();
// External config files
if (Files.exists(Paths.get("configs/settings.yml"))) {
propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml");
log.info("Settings file: {}", InstallationPathConfig.getSettingsPath());
if (Files.exists(Paths.get(InstallationPathConfig.getSettingsPath()))) {
propertyFiles.put(
"spring.config.additional-location",
"file:" + InstallationPathConfig.getSettingsPath());
} else {
log.warn("External configuration file 'configs/settings.yml' does not exist.");
log.warn(
"External configuration file '{}' does not exist.",
InstallationPathConfig.getSettingsPath());
}
if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
if (Files.exists(Paths.get(InstallationPathConfig.getCustomSettingsPath()))) {
String existingLocation =
propertyFiles.getOrDefault("spring.config.additional-location", "");
if (!existingLocation.isEmpty()) {
@@ -92,9 +106,11 @@ public class SPdfApplication {
}
propertyFiles.put(
"spring.config.additional-location",
existingLocation + "file:configs/custom_settings.yml");
existingLocation + "file:" + InstallationPathConfig.getCustomSettingsPath());
} else {
log.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
log.warn(
"Custom configuration file '{}' does not exist.",
InstallationPathConfig.getCustomSettingsPath());
}
Properties finalProps = new Properties();
if (!propertyFiles.isEmpty()) {
@@ -110,8 +126,8 @@ public class SPdfApplication {
app.run(args);
// Ensure directories are created
try {
Files.createDirectories(Path.of("customFiles/static/"));
Files.createDirectories(Path.of("customFiles/templates/"));
Files.createDirectories(Path.of(InstallationPathConfig.getTemplatesPath()));
Files.createDirectories(Path.of(InstallationPathConfig.getStaticPath()));
} catch (Exception e) {
log.error("Error creating directories: {}", e.getMessage());
}

View File

@@ -40,6 +40,7 @@ import me.friwi.jcefmaven.EnumProgress;
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.SPDF.config.InstallationPathConfig;
@Component
@Slf4j
@@ -72,7 +73,8 @@ public class DesktopBrowser implements WebBrowser {
CefAppBuilder builder = new CefAppBuilder();
configureCefSettings(builder);
builder.setProgressHandler(createProgressHandler());
builder.setInstallDir(
new File(InstallationPathConfig.getClientWebUIPath()));
// Build and initialize CEF
cefApp = builder.build();
client = cefApp.createClient();
@@ -99,8 +101,16 @@ public class DesktopBrowser implements WebBrowser {
private void configureCefSettings(CefAppBuilder builder) {
CefSettings settings = builder.getCefSettings();
settings.cache_path = new File("jcef-bundle").getAbsolutePath();
settings.root_cache_path = new File("jcef-bundle").getAbsolutePath();
String basePath = InstallationPathConfig.getClientWebUIPath();
log.info("basePath " + basePath);
settings.cache_path = new File(basePath + "cache").getAbsolutePath();
settings.root_cache_path = new File(basePath + "root_cache").getAbsolutePath();
// settings.browser_subprocess_path = new File(basePath +
// "subprocess").getAbsolutePath();
// settings.resources_dir_path = new File(basePath + "resources").getAbsolutePath();
// settings.locales_dir_path = new File(basePath + "locales").getAbsolutePath();
settings.log_file = new File(basePath, "debug.log").getAbsolutePath();
settings.persist_session_cookies = true;
settings.windowless_rendering_enabled = false;
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;
@@ -212,6 +222,9 @@ public class DesktopBrowser implements WebBrowser {
}
private void setupLoadHandler() {
final long initStartTime = System.currentTimeMillis();
log.info("Setting up load handler at: {}", initStartTime);
client.addLoadHandler(
new CefLoadHandlerAdapter() {
@Override
@@ -220,32 +233,77 @@ public class DesktopBrowser implements WebBrowser {
boolean isLoading,
boolean canGoBack,
boolean canGoForward) {
log.debug(
"Loading state change - isLoading: {}, canGoBack: {}, canGoForward: {}, "
+ "browserInitialized: {}, Time elapsed: {}ms",
isLoading,
canGoBack,
canGoForward,
browserInitialized,
System.currentTimeMillis() - initStartTime);
if (!isLoading && !browserInitialized) {
log.info(
"Browser finished loading, preparing to initialize UI components");
browserInitialized = true;
SwingUtilities.invokeLater(
() -> {
if (loadingWindow != null) {
Timer timer =
new Timer(
500,
e -> {
loadingWindow.dispose();
loadingWindow = null;
try {
if (loadingWindow != null) {
log.info("Starting UI initialization sequence");
frame.dispose();
frame.setOpacity(1.0f);
frame.setUndecorated(false);
frame.pack();
frame.setSize(1280, 800);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.requestFocus();
frame.toFront();
browser.getUIComponent()
.requestFocus();
});
timer.setRepeats(false);
timer.start();
// Close loading window first
loadingWindow.setVisible(false);
loadingWindow.dispose();
loadingWindow = null;
log.info("Loading window disposed");
// Then setup the main frame
frame.setVisible(false);
frame.dispose();
frame.setOpacity(1.0f);
frame.setUndecorated(false);
frame.pack();
frame.setSize(1280, 800);
frame.setLocationRelativeTo(null);
log.debug("Frame reconfigured");
// Show the main frame
frame.setVisible(true);
frame.requestFocus();
frame.toFront();
log.info("Main frame displayed and focused");
// Focus the browser component
Timer focusTimer =
new Timer(
100,
e -> {
try {
browser.getUIComponent()
.requestFocus();
log.info(
"Browser component focused");
} catch (Exception ex) {
log.error(
"Error focusing browser",
ex);
}
});
focusTimer.setRepeats(false);
focusTimer.start();
}
} catch (Exception e) {
log.error("Error during UI initialization", e);
// Attempt cleanup on error
if (loadingWindow != null) {
loadingWindow.dispose();
loadingWindow = null;
}
if (frame != null) {
frame.setVisible(true);
frame.requestFocus();
}
}
});
}

View File

@@ -14,9 +14,12 @@ public class LoadingWindow extends JDialog {
private final JLabel statusLabel;
private final JPanel mainPanel;
private final JLabel brandLabel;
private long startTime;
public LoadingWindow(Frame parent, String initialUrl) {
super(parent, "Initializing Stirling-PDF", true);
startTime = System.currentTimeMillis();
log.info("Creating LoadingWindow - initialization started at: {}", startTime);
// Initialize components
mainPanel = new JPanel();
@@ -29,8 +32,8 @@ public class LoadingWindow extends JDialog {
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5, 5, 5, 5);
gbc.weightx = 1.0; // Add horizontal weight
gbc.weighty = 0.0; // Add vertical weight
gbc.weightx = 1.0;
gbc.weighty = 0.0;
// Add icon
try {
@@ -43,12 +46,14 @@ public class LoadingWindow extends JDialog {
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
gbc.gridy = 0;
mainPanel.add(iconLabel, gbc);
log.debug("Icon loaded and scaled successfully");
}
}
}
} catch (Exception e) {
log.error("Failed to load icon", e);
}
// URL Label with explicit size
brandLabel = new JLabel(initialUrl);
brandLabel.setHorizontalAlignment(SwingConstants.CENTER);
@@ -63,6 +68,7 @@ public class LoadingWindow extends JDialog {
statusLabel.setPreferredSize(new Dimension(300, 25));
gbc.gridy = 2;
mainPanel.add(statusLabel, gbc);
// Progress bar with explicit size
progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true);
@@ -82,33 +88,78 @@ public class LoadingWindow extends JDialog {
setAlwaysOnTop(true);
setProgress(0);
setStatus("Starting...");
log.info(
"LoadingWindow initialization completed in {}ms",
System.currentTimeMillis() - startTime);
}
public void setProgress(final int progress) {
SwingUtilities.invokeLater(
() -> {
try {
progressBar.setValue(Math.min(Math.max(progress, 0), 100));
progressBar.setString(progress + "%");
int validProgress = Math.min(Math.max(progress, 0), 100);
log.info(
"Setting progress to {}% at {}ms since start",
validProgress, System.currentTimeMillis() - startTime);
// Log additional details when near 90%
if (validProgress >= 85 && validProgress <= 95) {
log.info(
"Near 90% progress - Current status: {}, Window visible: {}, "
+ "Progress bar responding: {}, Memory usage: {}MB",
statusLabel.getText(),
isVisible(),
progressBar.isEnabled(),
Runtime.getRuntime().totalMemory() / (1024 * 1024));
// Add thread state logging
Thread currentThread = Thread.currentThread();
log.debug(
"Current thread state - Name: {}, State: {}, Priority: {}",
currentThread.getName(),
currentThread.getState(),
currentThread.getPriority());
}
progressBar.setValue(validProgress);
progressBar.setString(validProgress + "%");
mainPanel.revalidate();
mainPanel.repaint();
} catch (Exception e) {
log.error("Error updating progress", e);
log.error("Error updating progress to " + progress, e);
}
});
}
public void setStatus(final String status) {
log.info(status);
log.info(
"Status update at {}ms - Setting status to: {}",
System.currentTimeMillis() - startTime,
status);
SwingUtilities.invokeLater(
() -> {
try {
statusLabel.setText(status != null ? status : "");
String validStatus = status != null ? status : "";
statusLabel.setText(validStatus);
// Log UI state when status changes
log.debug(
"UI State - Window visible: {}, Progress: {}%, Status: {}",
isVisible(), progressBar.getValue(), validStatus);
mainPanel.revalidate();
mainPanel.repaint();
} catch (Exception e) {
log.error("Error updating status", e);
log.error("Error updating status to: " + status, e);
}
});
}
@Override
public void dispose() {
log.info("LoadingWindow disposing after {}ms", System.currentTimeMillis() - startTime);
super.dispose();
}
}

View File

@@ -136,16 +136,6 @@ public class AppConfig {
return false;
}
@Bean(name = "watchedFoldersDir")
public String watchedFoldersDir() {
return "./pipeline/watchedFolders/";
}
@Bean(name = "finishedFoldersDir")
public String finishedFoldersDir() {
return "./pipeline/finishedFolders/";
}
@Bean(name = "directoryFilter")
public Predicate<Path> processOnlyFiles() {
return path -> {

View File

@@ -16,27 +16,15 @@ import org.simpleyaml.configuration.comments.CommentType;
import org.simpleyaml.configuration.file.YamlFile;
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ConfigInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
try {
ensureConfigExists();
} catch (Exception e) {
throw new RuntimeException("Failed to initialize application configuration", e);
}
}
public class ConfigInitializer {
public void ensureConfigExists() throws IOException, URISyntaxException {
// Define the path to the external config directory
Path destPath = Paths.get("configs", "settings.yml");
Path destPath = Paths.get(InstallationPathConfig.getSettingsPath());
// Check if the file already exists
if (Files.notExists(destPath)) {
@@ -53,10 +41,11 @@ public class ConfigInitializer
"Resource file not found: settings.yml.template");
}
}
log.info("Created settings file from template");
} else {
// Define the path to the config settings file
Path settingsPath = Paths.get("configs", "settings.yml");
Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath());
// Load the template resource
URL settingsTemplateResource =
getClass().getClassLoader().getResource("settings.yml.template");
@@ -120,7 +109,7 @@ public class ConfigInitializer
}
// Create custom settings file if it doesn't exist
Path customSettingsPath = Paths.get("configs", "custom_settings.yml");
Path customSettingsPath = Paths.get(InstallationPathConfig.getCustomSettingsPath());
if (!Files.exists(customSettingsPath)) {
Files.createFile(customSettingsPath);
}

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.config;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -265,20 +266,26 @@ public class EndpointConfiguration {
}
private void processEnvironmentConfigs() {
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
if (!bookAndHtmlFormatsInstalled) {
groupsToRemove.add("Calibre");
}
if (endpointsToRemove != null) {
for (String endpoint : endpointsToRemove) {
disableEndpoint(endpoint.trim());
}
}
if (applicationProperties != null && applicationProperties.getEndpoints() != null) {
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
if (groupsToRemove != null) {
for (String group : groupsToRemove) {
disableGroup(group.trim());
if (!bookAndHtmlFormatsInstalled) {
if (groupsToRemove == null) {
groupsToRemove = new ArrayList<>();
}
groupsToRemove.add("Calibre");
}
if (endpointsToRemove != null) {
for (String endpoint : endpointsToRemove) {
disableEndpoint(endpoint.trim());
}
}
if (groupsToRemove != null) {
for (String group : groupsToRemove) {
disableGroup(group.trim());
}
}
}
}

View File

@@ -33,7 +33,8 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe
String characterEncoding,
Map<String, Object> templateResolutionAttributes) {
Resource resource =
resourceLoader.getResource("file:./customFiles/templates/" + resourceName);
resourceLoader.getResource(
"file:" + InstallationPathConfig.getTemplatesPath() + resourceName);
try {
if (resource.exists() && resource.isReadable()) {
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);

View File

@@ -0,0 +1,132 @@
package stirling.software.SPDF.config;
import java.io.File;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class InstallationPathConfig {
private static final String BASE_PATH;
// Root paths
private static final String LOG_PATH;
private static final String CONFIG_PATH;
private static final String PIPELINE_PATH;
private static final String CUSTOM_FILES_PATH;
private static final String CLIENT_WEBUI_PATH;
// Config paths
private static final String SETTINGS_PATH;
private static final String CUSTOM_SETTINGS_PATH;
// Pipeline paths
private static final String PIPELINE_WATCHED_FOLDERS_PATH;
private static final String PIPELINE_FINISHED_FOLDERS_PATH;
// Custom file paths
private static final String STATIC_PATH;
private static final String TEMPLATES_PATH;
private static final String SIGNATURES_PATH;
static {
BASE_PATH = initializeBasePath();
// Initialize root paths
LOG_PATH = BASE_PATH + "logs" + File.separator;
CONFIG_PATH = BASE_PATH + "configs" + File.separator;
PIPELINE_PATH = BASE_PATH + "pipeline" + File.separator;
CUSTOM_FILES_PATH = BASE_PATH + "customFiles" + File.separator;
CLIENT_WEBUI_PATH = BASE_PATH + "clientWebUI" + File.separator;
// Initialize config paths
SETTINGS_PATH = CONFIG_PATH + "settings.yml";
CUSTOM_SETTINGS_PATH = CONFIG_PATH + "custom_settings.yml";
// Initialize pipeline paths
PIPELINE_WATCHED_FOLDERS_PATH = PIPELINE_PATH + "watchedFolders" + File.separator;
PIPELINE_FINISHED_FOLDERS_PATH = PIPELINE_PATH + "finishedFolders" + File.separator;
// Initialize custom file paths
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator;
SIGNATURES_PATH = CUSTOM_FILES_PATH + "signatures" + File.separator;
}
private static String initializeBasePath() {
if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
return System.getenv("APPDATA") + File.separator + "Stirling-PDF" + File.separator;
} else if (os.contains("mac")) {
return System.getProperty("user.home")
+ File.separator
+ "Library"
+ File.separator
+ "Application Support"
+ File.separator
+ "Stirling-PDF"
+ File.separator;
} else {
return System.getProperty("user.home")
+ File.separator
+ ".config"
+ File.separator
+ "Stirling-PDF"
+ File.separator;
}
}
return "./";
}
public static String getPath() {
return BASE_PATH;
}
public static String getLogPath() {
return LOG_PATH;
}
public static String getConfigPath() {
return CONFIG_PATH;
}
public static String getPipelinePath() {
return PIPELINE_PATH;
}
public static String getCustomFilesPath() {
return CUSTOM_FILES_PATH;
}
public static String getClientWebUIPath() {
return CLIENT_WEBUI_PATH;
}
public static String getSettingsPath() {
return SETTINGS_PATH;
}
public static String getCustomSettingsPath() {
return CUSTOM_SETTINGS_PATH;
}
public static String getPipelineWatchedFoldersDir() {
return PIPELINE_WATCHED_FOLDERS_PATH;
}
public static String getPipelineFinishedFoldersDir() {
return PIPELINE_FINISHED_FOLDERS_PATH;
}
public static String getStaticPath() {
return STATIC_PATH;
}
public static String getTemplatesPath() {
return TEMPLATES_PATH;
}
public static String getSignaturesPath() {
return SIGNATURES_PATH;
}
}

View File

@@ -0,0 +1,10 @@
package stirling.software.SPDF.config;
import ch.qos.logback.core.PropertyDefinerBase;
public class LogbackPropertyLoader extends PropertyDefinerBase {
@Override
public String getPropertyValue() {
return InstallationPathConfig.getLogPath();
}
}

View File

@@ -23,7 +23,8 @@ public class WebMvcConfig implements WebMvcConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Handler for external static resources
registry.addResourceHandler("/**")
.addResourceLocations("file:customFiles/static/", "classpath:/static/");
.addResourceLocations(
"file:" + InstallationPathConfig.getStaticPath(), "classpath:/static/");
// .setCachePeriod(0); // Optional: disable caching
}
}

View File

@@ -16,7 +16,6 @@ public class YamlPropertySourceFactory implements PropertySourceFactory {
throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(

View File

@@ -22,22 +22,31 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
import stirling.software.SPDF.model.User;
@Slf4j
@Component
public class UserAuthenticationFilter extends OncePerRequestFilter {
private final ApplicationProperties applicationProperties;
private final UserService userService;
private final SessionPersistentRegistry sessionPersistentRegistry;
private final boolean loginEnabledValue;
public UserAuthenticationFilter(
@Lazy ApplicationProperties applicationProperties,
@Lazy UserService userService,
SessionPersistentRegistry sessionPersistentRegistry,
@Qualifier("loginEnabled") boolean loginEnabledValue) {
this.applicationProperties = applicationProperties;
this.userService = userService;
this.sessionPersistentRegistry = sessionPersistentRegistry;
this.loginEnabledValue = loginEnabledValue;
@@ -121,33 +130,67 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// Check if the authenticated user is disabled and invalidate their session if so
if (authentication != null && authentication.isAuthenticated()) {
Security securityProp = applicationProperties.getSecurity();
LoginMethod loginMethod = LoginMethod.UNKNOWN;
boolean blockRegistration = false;
// Extract username and determine the login method
Object principal = authentication.getPrincipal();
String username = null;
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
loginMethod = LoginMethod.USERDETAILS;
} else if (principal instanceof OAuth2User) {
username = ((OAuth2User) principal).getName();
loginMethod = LoginMethod.OAUTH2USER;
OAUTH2 oAuth = securityProp.getOauth2();
blockRegistration = oAuth != null && oAuth.getBlockRegistration();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
loginMethod = LoginMethod.SAML2USER;
SAML2 saml2 = securityProp.getSaml2();
blockRegistration = saml2 != null && saml2.getBlockRegistration();
} else if (principal instanceof String) {
username = (String) principal;
loginMethod = LoginMethod.STRINGUSER;
}
// Retrieve all active sessions for the user
List<SessionInformation> sessionsInformations =
sessionPersistentRegistry.getAllSessions(principal, false);
// Check if the user exists, is disabled, or needs session invalidation
if (username != null) {
log.debug("Validating user: {}", username);
boolean isUserExists = userService.usernameExistsIgnoreCase(username);
boolean isUserDisabled = userService.isUserDisabled(username);
boolean notSsoLogin =
!loginMethod.equals(LoginMethod.OAUTH2USER)
&& !loginMethod.equals(LoginMethod.SAML2USER);
// Block user registration if not allowed by configuration
if (blockRegistration && !isUserExists) {
log.warn("Blocked registration for OAuth2/SAML user: {}", username);
response.sendRedirect(
request.getContextPath() + "/logout?oauth2_admin_blocked_user=true");
return;
}
// Expire sessions and logout if the user does not exist or is disabled
if (!isUserExists || isUserDisabled) {
log.info(
"Invalidating session for disabled or non-existent user: {}", username);
for (SessionInformation sessionsInformation : sessionsInformations) {
sessionsInformation.expireNow();
sessionPersistentRegistry.expireSession(sessionsInformation.getSessionId());
}
}
if (!isUserExists) {
// Redirect to logout if credentials are invalid
if (!isUserExists && notSsoLogin) {
response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true");
return;
}
@@ -161,6 +204,25 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response);
}
private enum LoginMethod {
USERDETAILS("UserDetails"),
OAUTH2USER("OAuth2User"),
STRINGUSER("StringUser"),
UNKNOWN("Unknown"),
SAML2USER("Saml2User");
private String method;
LoginMethod(String method) {
this.method = method;
}
@Override
public String toString() {
return method;
}
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String uri = request.getRequestURI();

View File

@@ -11,13 +11,11 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.User;
@Component
@Slf4j
public class CustomSaml2ResponseAuthenticationConverter
implements Converter<ResponseToken, Saml2Authentication> {

View File

@@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.GeneralUtils;
@@ -33,7 +34,8 @@ public class SettingsController {
if (!"undefined".equals(applicationProperties.getSystem().getEnableAnalytics())) {
return ResponseEntity.status(HttpStatus.ALREADY_REPORTED)
.body(
"Setting has already been set, To adjust please edit /config/settings.yml");
"Setting has already been set, To adjust please edit "
+ InstallationPathConfig.getSettingsPath());
}
GeneralUtils.saveKeyToConfig("system.enableAnalytics", String.valueOf(enabled), false);
applicationProperties.getSystem().setEnableAnalytics(String.valueOf(enabled));

View File

@@ -13,6 +13,9 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.rendering.ImageType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@@ -31,11 +34,8 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.CheckProgramInstall;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.*;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@RequestMapping("/api/v1/convert")
@@ -62,14 +62,20 @@ public class ConvertImgPDFController {
String singleOrMultiple = request.getSingleOrMultiple();
String colorType = request.getColorType();
String dpi = request.getDpi();
String pageNumbers = request.getPageNumbers();
Path tempFile = null;
Path tempOutputDir = null;
Path tempPdfPath = null;
byte[] result = null;
String[] pageOrderArr =
(pageNumbers != null && !pageNumbers.trim().isEmpty())
? pageNumbers.split(",")
: new String[] {"all"};
;
try {
byte[] pdfBytes = file.getBytes();
// Load the input PDF
byte[] newPdfBytes = rearrangePdfPages(file.getBytes(), pageOrderArr);
ImageType colorTypeResult = ImageType.RGB;
if ("greyscale".equals(colorType)) {
colorTypeResult = ImageType.GRAY;
@@ -84,7 +90,7 @@ public class ConvertImgPDFController {
result =
PdfUtils.convertFromPdf(
pdfBytes,
newPdfBytes,
"webp".equalsIgnoreCase(imageFormat)
? "png"
: imageFormat.toUpperCase(),
@@ -227,4 +233,46 @@ public class ConvertImgPDFController {
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
return "null".equals(mimeType) ? "application/octet-stream" : mimeType;
}
/**
* Rearranges the pages of the given PDF document based on the specified page order.
*
* @param pdfBytes The byte array of the original PDF file.
* @param pageOrderArr An array of page numbers indicating the new order.
* @return A byte array of the rearranged PDF.
* @throws IOException If an error occurs while processing the PDF.
*/
private byte[] rearrangePdfPages(byte[] pdfBytes, String[] pageOrderArr) throws IOException {
// Load the input PDF
PDDocument document = Loader.loadPDF(pdfBytes);
int totalPages = document.getNumberOfPages();
List<Integer> newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
// Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>();
for (int pageIndex : newPageOrder) {
newPages.add(document.getPage(pageIndex));
}
// Remove all the pages from the original document
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
document.removePage(i);
}
// Add the pages in the new order
for (PDPage page : newPages) {
document.addPage(page);
}
// Convert PDDocument to byte array
byte[] newPdfBytes;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
document.save(baos);
newPdfBytes = baos.toByteArray();
} finally {
document.close();
}
return newPdfBytes;
}
}

View File

@@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.api.HandleDataRequest;
import stirling.software.SPDF.utils.WebResponseUtils;
@@ -35,22 +34,12 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Pipeline", description = "Pipeline APIs")
public class PipelineController {
final String watchedFoldersDir = "./pipeline/watchedFolders/";
final String finishedFoldersDir = "./pipeline/finishedFolders/";
private final PipelineProcessor processor;
private final ApplicationProperties applicationProperties;
private final ObjectMapper objectMapper;
public PipelineController(
PipelineProcessor processor,
ApplicationProperties applicationProperties,
ObjectMapper objectMapper) {
public PipelineController(PipelineProcessor processor, ObjectMapper objectMapper) {
this.processor = processor;
this.applicationProperties = applicationProperties;
this.objectMapper = objectMapper;
}

View File

@@ -16,7 +16,6 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.Scheduled;
@@ -25,6 +24,7 @@ import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.utils.FileMonitor;
@@ -48,14 +48,12 @@ public class PipelineDirectoryProcessor {
public PipelineDirectoryProcessor(
ObjectMapper objectMapper,
ApiDocService apiDocService,
@Qualifier("watchedFoldersDir") String watchedFoldersDir,
@Qualifier("finishedFoldersDir") String finishedFoldersDir,
PipelineProcessor processor,
FileMonitor fileMonitor) {
this.objectMapper = objectMapper;
this.apiDocService = apiDocService;
this.watchedFoldersDir = watchedFoldersDir;
this.finishedFoldersDir = finishedFoldersDir;
this.watchedFoldersDir = InstallationPathConfig.getPipelineWatchedFoldersDir();
this.finishedFoldersDir = InstallationPathConfig.getPipelineFinishedFoldersDir();
this.processor = processor;
this.fileMonitor = fileMonitor;
}

View File

@@ -163,6 +163,9 @@ public class AccountWebController {
case "invalid_destination":
erroroauth = "login.invalid_destination";
break;
case "relying_party_registration_not_found":
erroroauth = "login.relyingPartyRegistrationNotFound";
break;
// Valid InResponseTo was not available from the validation context, unable to
// evaluate
case "invalid_in_response_to":

View File

@@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.SignatureFile;
import stirling.software.SPDF.service.SignatureService;
@@ -34,8 +35,6 @@ import stirling.software.SPDF.service.SignatureService;
@Slf4j
public class GeneralWebController {
private static final String SIGNATURE_BASE_PATH = "customFiles/static/signatures/";
private static final String ALL_USERS_FOLDER = "ALL_USERS";
private final SignatureService signatureService;
private final UserServiceInterface userService;
private final ResourceLoader resourceLoader;
@@ -223,7 +222,9 @@ public class GeneralWebController {
// Extract font names from classpath
fontNames.addAll(getFontNamesFromLocation("classpath:static/fonts/*.woff2"));
// Extract font names from external directory
fontNames.addAll(getFontNamesFromLocation("file:customFiles/static/fonts/*"));
fontNames.addAll(
getFontNamesFromLocation(
"file:" + InstallationPathConfig.getStaticPath() + "fonts/*"));
return fontNames;
}

View File

@@ -1,5 +1,7 @@
package stirling.software.SPDF.model;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
@@ -13,18 +15,23 @@ import java.util.List;
import java.util.stream.Collectors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.config.YamlPropertySourceFactory;
import stirling.software.SPDF.model.provider.GithubProvider;
import stirling.software.SPDF.model.provider.GoogleProvider;
@@ -33,11 +40,37 @@ import stirling.software.SPDF.model.provider.UnsupportedProviderException;
@Configuration
@ConfigurationProperties(prefix = "")
@PropertySource(value = "file:./configs/settings.yml", factory = YamlPropertySourceFactory.class)
@Data
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class ApplicationProperties {
@Bean
public PropertySource<?> dynamicYamlPropertySource(ConfigurableEnvironment environment)
throws IOException {
String configPath = InstallationPathConfig.getSettingsPath();
log.debug("Attempting to load settings from: " + configPath);
File file = new File(configPath);
if (!file.exists()) {
log.error("Warning: Settings file does not exist at: " + configPath);
}
Resource resource = new FileSystemResource(configPath);
if (!resource.exists()) {
throw new FileNotFoundException("Settings file not found at: " + configPath);
}
EncodedResource encodedResource = new EncodedResource(resource);
PropertySource<?> propertySource =
new YamlPropertySourceFactory().createPropertySource(null, encodedResource);
environment.getPropertySources().addFirst(propertySource);
log.debug("Loaded properties: " + propertySource.getSource());
return propertySource;
}
private Legal legal = new Legal();
private Security security = new Security();
private System system = new System();
@@ -153,6 +186,7 @@ public class ApplicationProperties {
}
public Resource getSpCert() {
if (spCert == null) return null;
if (spCert.startsWith("classpath:")) {
return new ClassPathResource(spCert.substring("classpath:".length()));
} else {
@@ -161,6 +195,7 @@ public class ApplicationProperties {
}
public Resource getidpCert() {
if (idpCert == null) return null;
if (idpCert.startsWith("classpath:")) {
return new ClassPathResource(idpCert.substring("classpath:".length()));
} else {

View File

@@ -21,6 +21,11 @@ public class ConvertToImageRequest extends PDFFile {
allowableValues = {"single", "multiple"})
private String singleOrMultiple;
@Schema(
description =
"The pages to select, Supports ranges (e.g., '1,3,5-9'), or 'all' or functions in the format 'an+b' where 'a' is the multiplier of the page number 'n', and 'b' is a constant (e.g., '2n+1', '3n', '6n-5')\"")
private String pageNumbers;
@Schema(
description = "The color type of the output image(s)",
allowableValues = {"color", "greyscale", "blackwhite"})

View File

@@ -13,14 +13,19 @@ import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.SignatureFile;
@Service
@Slf4j
public class SignatureService {
private static final String SIGNATURE_BASE_PATH = "customFiles/signatures/";
private static final String ALL_USERS_FOLDER = "ALL_USERS";
private final String SIGNATURE_BASE_PATH;
private final String ALL_USERS_FOLDER = "ALL_USERS";
public SignatureService() {
SIGNATURE_BASE_PATH = InstallationPathConfig.getSignaturesPath();
}
public boolean hasAccessToFile(String username, String fileName) throws IOException {
validateFileName(fileName);

View File

@@ -15,6 +15,7 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
@Component
@Slf4j
@@ -34,9 +35,7 @@ public class FileMonitor {
* monitored, false otherwise
*/
@Autowired
public FileMonitor(
@Qualifier("watchedFoldersDir") String rootDirectory,
@Qualifier("directoryFilter") Predicate<Path> pathFilter)
public FileMonitor(@Qualifier("directoryFilter") Predicate<Path> pathFilter)
throws IOException {
this.newlyDiscoveredFiles = new HashSet<>();
this.path2KeyMapping = new HashMap<>();
@@ -44,7 +43,7 @@ public class FileMonitor {
this.pathFilter = pathFilter;
this.readyForProcessingFiles = ConcurrentHashMap.newKeySet();
this.watchService = FileSystems.getDefault().newWatchService();
this.rootDir = Path.of(rootDirectory);
this.rootDir = Path.of(InstallationPathConfig.getPipelineWatchedFoldersDir());
}
private boolean shouldNotProcess(Path path) {

View File

@@ -26,6 +26,7 @@ import io.github.pixee.security.HostValidator;
import io.github.pixee.security.Urls;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
@Slf4j
public class GeneralUtils {
@@ -83,7 +84,7 @@ public class GeneralUtils {
// Allow only http and https protocols
String protocol = url.getProtocol();
if (!protocol.equals("http") && !protocol.equals("https")) {
if (!"http".equals(protocol) && !"https".equals(protocol)) {
return false; // Disallow other protocols
}
@@ -220,32 +221,54 @@ public class GeneralUtils {
throw new IllegalArgumentException("Invalid expression");
}
int n = 0;
while (true) {
for (int n = 1; n <= maxValue; n++) {
// Replace 'n' with the current value of n, correctly handling numbers before
// 'n'
String sanitizedExpression = insertMultiplicationBeforeN(expression, n);
String sanitizedExpression = sanitizeNFunction(expression, n);
Double result = evaluator.evaluate(sanitizedExpression);
// Check if the result is null or not within bounds
if (result == null || result <= 0 || result.intValue() > maxValue) {
if (n != 0) break;
} else {
if (result == null) break;
if (result.intValue() > 0 && result.intValue() <= maxValue)
results.add(result.intValue());
}
n++;
}
return results;
}
private static String sanitizeNFunction(String expression, int nValue) {
String sanitizedExpression = expression.replace(" ", "");
String multiplyByOpeningRoundBracketPattern =
"([0-9n)])\\("; // example: n(n-1), 9(n-1), (n-1)(n-2)
sanitizedExpression =
sanitizedExpression.replaceAll(multiplyByOpeningRoundBracketPattern, "$1*(");
String multiplyByClosingRoundBracketPattern =
"\\)([0-9n)])"; // example: (n-1)n, (n-1)9, (n-1)(n-2)
sanitizedExpression =
sanitizedExpression.replaceAll(multiplyByClosingRoundBracketPattern, ")*$1");
sanitizedExpression = insertMultiplicationBeforeN(sanitizedExpression, nValue);
return sanitizedExpression;
}
private static String insertMultiplicationBeforeN(String expression, int nValue) {
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
withMultiplication = formatConsecutiveNsForNFunction(withMultiplication);
// Now replace 'n' with its current value
return withMultiplication.replace("n", String.valueOf(nValue));
}
private static String formatConsecutiveNsForNFunction(String expression) {
String text = expression;
while (text.matches(".*n{2,}.*")) {
text = text.replaceAll("(?<!n)n{2}", "n*n");
}
return text;
}
private static List<Integer> handlePart(String part, int totalPages, int offset) {
List<Integer> partResult = new ArrayList<>();
@@ -320,7 +343,10 @@ public class GeneralUtils {
public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
throws IOException {
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
Path path =
Paths.get(
InstallationPathConfig
.getSettingsPath()); // Target the configs/settings.yml
final YamlFile settingsYml = new YamlFile(path.toFile());
DumperOptions yamlOptionssettingsYml =
@@ -338,7 +364,7 @@ public class GeneralUtils {
public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated)
throws IOException {
Path path = Paths.get("configs", "settings.yml");
Path path = Paths.get(InstallationPathConfig.getSettingsPath());
final YamlFile settingsYml = new YamlFile(path.toFile());
DumperOptions yamlOptionssettingsYml =

View File

@@ -1,3 +0,0 @@
package stirling.software.SPDF.utils;
public class PDFManipulationUtils {}

View File

@@ -9,6 +9,7 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -64,7 +65,7 @@ public class PDFToFile {
.runCommandWithOutputHandling(command, tempOutputDir.toFile());
// Get output files
List<File> outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles());
File[] outputFiles = Objects.requireNonNull(tempOutputDir.toFile().listFiles());
// Return output files in a ZIP archive
fileName = pdfBaseName + "ToHtml.zip";

View File

@@ -22,9 +22,11 @@ import org.apache.pdfbox.text.TextPosition;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
@Slf4j
public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
private String textColor;
@@ -93,17 +95,17 @@ public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
try {
font = PDFontFactory.createFont(text.getFont().getCOSObject());
} catch (IOException io) {
System.out.println("Primary font not found, using fallback font.");
log.info("Primary font not found, using fallback font.");
font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
}
// if a character is not supported by font, then look for supported font
try {
byte[] bytes = font.encode(unicodeText);
} catch (IOException io) {
System.out.println("text could not be encoded ");
log.info("text could not be encoded ");
font = checkSupportedFontForCharacter(unicodeText);
} catch (IllegalArgumentException ie) {
System.out.println("text not supported by font ");
log.info("text not supported by font ");
font = checkSupportedFontForCharacter(unicodeText);
} finally {
// if any other font is not supported, then replace default character *
@@ -157,9 +159,9 @@ public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
byte[] bytes = currentFont.encode(unicodeText);
return currentFont;
} catch (IOException io) {
System.out.println("text could not be encoded ");
log.info("text could not be encoded ");
} catch (IllegalArgumentException ie) {
System.out.println("text not supported by font ");
log.info("text not supported by font ");
}
}
return null;

View File

@@ -27,8 +27,7 @@ spring.devtools.restart.exclude=stirling.software.SPDF.config.security/**
spring.thymeleaf.encoding=UTF-8
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
#spring.thymeleaf.cache=false
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<define name="LOG_PATH" class="stirling.software.SPDF.config.LogbackPropertyLoader" />
<!-- Console Appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
@@ -7,35 +9,30 @@
</encoder>
</appender>
<!-- Rolling File Appender -->
<!-- Rolling File Appender for Auth Logs -->
<appender name="AUTHLOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/invalid-auths.log</file>
<file>${LOG_PATH}/invalid-auths.log</file>
<encoder>
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover and keep 7 days' worth of history -->
<fileNamePattern>logs/auth-%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${LOG_PATH}/auth-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>1</maxHistory>
</rollingPolicy>
</appender>
<!-- Rolling File Appender -->
<!-- Rolling File Appender for General Logs -->
<appender name="GENERAL" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/info.log</file>
<file>${LOG_PATH}/info.log</file>
<encoder>
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover and keep 7 days' worth of history -->
<fileNamePattern>logs/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${LOG_PATH}/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>1</maxHistory>
</rollingPolicy>
</appender>
<!-- Root Logger -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
@@ -43,10 +40,9 @@
</root>
<!-- Specific Logger -->
<logger name="stirling.software.SPDF.config.security.CustomAuthenticationFailureHandler" level="ERROR"
additivity="false">
<logger name="stirling.software.SPDF.config.security.CustomAuthenticationFailureHandler"
level="ERROR" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="AUTHLOG"/>
</logger>
</configuration>
</configuration>

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=طلب غير صالح
login.oauth2AccessDenied=تم رفض الوصول
login.oauth2InvalidTokenResponse=استجابة الرمز المميز غير صالحة
login.oauth2InvalidIdToken=رمز الهوية غير صالح
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=تم تعطيل المستخدم، تم حظر تسجيل الدخول حاليًا باستخدام اسم المستخدم هذا. يرجى الاتصال بالمسؤول.
login.alreadyLoggedIn=لقد تسجل دخولًا إلى
login.alreadyLoggedIn2=أجهزة أخرى. يرجى تسجيل الخروج من الأجهزة وحاول مرة أخرى.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=إصلاح
repair.header=إصلاح ملفات PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=تدرج الرمادي
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
pdfToImage.submit=تحويل
pdfToImage.info=Python غير مثبت. مطلوب لتحويل WebP.
pdfToImage.placeholder=(مثال: 1,2,8 أو 4,7,12-16 أو 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=تقطيع ملف PDF
fileChooser.click=انقر هنا
fileChooser.or=أو
fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Etibarsız Sorğu
login.oauth2AccessDenied=Giriş rədd edildi
login.oauth2InvalidTokenResponse=Etibarsız Token Cavabı
login.oauth2InvalidIdToken=Etibarsız Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=İstifadəçi deaktivləşdirilmişdir, bu istifadəçi adı ilə giriş hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın.
login.alreadyLoggedIn=Siz artıq daxil olmusunuz
login.alreadyLoggedIn2=cihazlar. Zəhmət olmasa, cihazlardan çıxış edin və yenidən cəhd edin.
@@ -830,7 +831,7 @@ sign.first=İlk səhifə
sign.last=Son səhifə
sign.next=Növbəti səhifə
sign.previous=Əvvəlki səhifə
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Bərpa Et
repair.header=PDFləri Bərpa Et
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Boz Tonlama
pdfToImage.blackwhite=Qara və Ağ (Data İtə Bilər)
pdfToImage.submit=Çevir
pdfToImage.info=Python Yüklü Deyil.WebP Çevirməsi Üçün Vacibdir
pdfToImage.placeholder=(məsələn, 1,2,8 və ya 4,7,12-16 və ya 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF-i Ayır
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Невалидна заявка
login.oauth2AccessDenied=Отказан достъп
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Потребителят е деактивиран, влизането в момента е блокирано с това потребителско име. Моля, свържете се с администратора.
login.alreadyLoggedIn=Вече сте влезли в
login.alreadyLoggedIn2=устройства. Моля, излезте от устройствата и опитайте отново.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Поправи
repair.header=Поправи PDF-и
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Скала на сивото
pdfToImage.blackwhite=Черно и бяло (може да загубите данни!)
pdfToImage.submit=Преобразуване
pdfToImage.info=Python не е инсталиран. Изисква се за конвертиране на WebP.
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Разделяне на PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Sol·licitud no vàlida
login.oauth2AccessDenied=Accés denegat
login.oauth2InvalidTokenResponse=Resposta de token no vàlida
login.oauth2InvalidIdToken=ID Token no vàlid
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=L'usuari està desactivat, l'inici de sessió està actualment bloquejat amb aquest nom d'usuari. Si us plau, contacta amb l'administrador.
login.alreadyLoggedIn=Ja has iniciat sessió a
login.alreadyLoggedIn2=dispositius. Si us plau, tanca la sessió en els dispositius i torna-ho a intentar.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparar
repair.header=Repara els PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de Grisos
pdfToImage.blackwhite=Blanc i Negre (Pot perdre dades!)
pdfToImage.submit=Converteix
pdfToImage.info=Python no està instal·lat. És necessari per a la conversió a WebP.
pdfToImage.placeholder=(p. ex. 1,2,8 o 4,7,12-16 o 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Divideix PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Neplatný požadavek
login.oauth2AccessDenied=Přístup zazobán
login.oauth2InvalidTokenResponse=Neplatná odpověď tokenu
login.oauth2InvalidIdToken=Neplatný identifikační token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Uživatel je deaktivován, přihlášení aktuálně pro tuto uživatelskou jmena je zakázáno. Kontaktujte správce.
login.alreadyLoggedIn=Jste již přihlášeni na
login.alreadyLoggedIn2=zariadení. Odhlašujte se z těchto zařízení a zkuste to znovu.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Opravit
repair.header=Opravit PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Stupně šedi
pdfToImage.blackwhite=Černobílý (Může dojít k ztrátě dat!)
pdfToImage.submit=Převést
pdfToImage.info=Python není nainstalován. Potřebuje se pro konverzi do WebP.
pdfToImage.placeholder=(např. 1,2,8 nebo 4,7,12-16 nebo 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Podělit se PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ugyldig Anmodning
login.oauth2AccessDenied=Adgang Nægtet
login.oauth2InvalidTokenResponse=Ugyldigt Token Svar
login.oauth2InvalidIdToken=Ugyldigt Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Bruger er deaktiveret, login er i øjeblikket blokeret med dette brugernavn. Kontakt venligst administratoren.
login.alreadyLoggedIn=Du er allerede logget ind på
login.alreadyLoggedIn2=enheder. Log ud af disse enheder og prøv igen.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparér
repair.header=Reparér PDF'er
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gråtone
pdfToImage.blackwhite=Sort og Hvid (Kan miste data!)
pdfToImage.submit=Konvertér
pdfToImage.info=Python er ikke installeret. Påkrævet for WebP-konvertering.
pdfToImage.placeholder=(f.eks. 1,2,8 eller 4,7,12-16 eller 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Splitter PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=ungültige Anfrage
login.oauth2AccessDenied=Zugriff abgelehnt
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
login.oauth2InvalidIdToken=Ungültiges ID-Token
login.relyingPartyRegistrationNotFound=Keine Relying-Party-Registrierung gefunden
login.userIsDisabled=Benutzer ist deaktiviert, die Anmeldung ist mit diesem Benutzernamen derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
login.alreadyLoggedIn=Sie sind bereits an
login.alreadyLoggedIn2=Geräten angemeldet. Bitte melden Sie sich dort ab und versuchen es dann erneut.
@@ -830,7 +831,7 @@ sign.first=Erste Seite
sign.last=Letzte Seite
sign.next=Nächste Seite
sign.previous=Vorherige Seite
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparieren
repair.header=PDFs reparieren
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Graustufen
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
pdfToImage.submit=Umwandeln
pdfToImage.info=Python ist nicht installiert. Erforderlich für die WebP-Konvertierung.
pdfToImage.placeholder=(z.B. 1,2,8 oder 4,7,12-16 oder 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF teilen
fileChooser.click=Klicken
fileChooser.or=oder
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Αρχαίως απαιτήσεις πληροφορ
login.oauth2AccessDenied=Πρόσβαση ορισμένες τοποθετήσεις
login.oauth2InvalidTokenResponse=Απάντηση άρκετο ρόταμα
login.oauth2InvalidIdToken=Λιδός όρκου μη αρκετός
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Ο χρήστης είναι δευτεροβαθμιακά διακωμένος, το σύστημα άλλαξε τον καθώς βρεθεί. Παρακαλείστε τους αρχηγούς να επιβεβαιώσουν τη συμπεριφορά χρήστη.
login.alreadyLoggedIn=Είστε ήδη σύνδεδες σε
login.alreadyLoggedIn2=κατοχόι. Παρακαλώ δυσκέντρωση τους και προσπαθήστε ξανά.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Επιδιόρθωση
repair.header=Επιδιόρθωση PDFs
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Κλίμακα του γκρι
pdfToImage.blackwhite=Ασπρόμαυρο (Μπορεί να χαθούν δεδομένα!)
pdfToImage.submit=Μετατροπή
pdfToImage.info=Δεν είναι ιστάμενος Python. Είναι απαιτήτων για τη μετατροπή σε WebP.
pdfToImage.placeholder=(π.χ. 1,2,8 ή 4,7,12-16 ή 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Διαλύστε το PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Repair
repair.header=Repair PDFs
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Greyscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.submit=Convert
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Repair
repair.header=Repair PDFs
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Grayscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.submit=Convert
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Solicitud no válida
login.oauth2AccessDenied=Acceso denegado
login.oauth2InvalidTokenResponse=Respuesta de token no válida
login.oauth2InvalidIdToken=Token de identificación no válido
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador.
login.alreadyLoggedIn=Ya has iniciado sesión en
login.alreadyLoggedIn2=dispositivos. Cierra sesión en los dispositivos y vuelve a intentarlo.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparar
repair.header=Reparar archivos PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de grises
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
pdfToImage.submit=Convertir
pdfToImage.info=Python no está instalado. Se requiere para la conversión WebP.
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividir PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Konpondu
repair.header=Konpondu PDF fitxategiak
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gris-eskala
pdfToImage.blackwhite=Zuria eta Beltza (Datuak galdu ditzake!)
pdfToImage.submit=Bihurtu
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=درخواست نامعتبر
login.oauth2AccessDenied=دسترسی ممنوع
login.oauth2InvalidTokenResponse=پاسخ توکن نامعتبر است
login.oauth2InvalidIdToken=توکن شناسه نامعتبر است
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=کاربر غیرفعال شده است، ورود با این نام کاربری در حال حاضر مسدود است. لطفاً با مدیر تماس بگیرید.
login.alreadyLoggedIn=شما قبلاً وارد شده‌اید در
login.alreadyLoggedIn2=دستگاه‌ها. لطفاً از دستگاه‌ها خارج شده و دوباره تلاش کنید.
@@ -830,7 +831,7 @@ sign.first=صفحه اول
sign.last=صفحه آخر
sign.next=صفحه بعدی
sign.previous=صفحه قبلی
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=تعمیر
repair.header=تعمیر PDFها
@@ -1041,6 +1042,7 @@ pdfToImage.grey=خاکستری
pdfToImage.blackwhite=سیاه و سفید (ممکن است اطلاعات از دست برود!)
pdfToImage.submit=تبدیل
pdfToImage.info=پایتون نصب نشده است. برای تبدیل WebP لازم است.
pdfToImage.placeholder=(مثال: 1,2,8 یا 4,7,12-16 یا 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=تقسیم PDF
fileChooser.click=کلیک کنید
fileChooser.or=یا
fileChooser.dragAndDrop=بکشید و رها کنید
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=فایل(های) خود را اینجا بکشید و رها کنید
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requête invalide
login.oauth2AccessDenied=Accès refusé
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
login.oauth2InvalidIdToken=Jeton d'identification invalide
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=L'utilisateur est désactivé, la connexion est actuellement bloquée avec ce nom d'utilisateur. Veuillez contacter l'administrateur.
login.alreadyLoggedIn=Vous êtes déjà connecté sur
login.alreadyLoggedIn2=appareils. Veuillez vous déconnecter des appareils et réessayer.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Réparer
repair.header=Réparer
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Niveaux de gris
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !)
pdfToImage.submit=Convertir
pdfToImage.info=Python nest pas installé. Nécessaire pour la conversion WebP.
pdfToImage.placeholder=(par exemple : 1,2,8 ou 4,7,12-16 ou 2n-1)
#addPassword
@@ -1281,49 +1283,51 @@ splitByChapters.desc.4=Autoriser les Doublons : Si coché, permet à plusieurs s
splitByChapters.submit=Diviser le PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.click=Cliquez
fileChooser.or=ou
fileChooser.dragAndDrop=Glisser & Déposer
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Glisser & Déposer le(s) fichier(s) ici
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English
releases.footer=Versions
releases.title=Notes de version
releases.header=Notes de version
releases.current.version=Version actuelle
releases.note=Les notes de version sont uniquement disponibles en anglais
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.title=Valider les signatures PDF
validateSignature.header=Valider les signatures numériques
validateSignature.selectPDF=Sélectionnez un fichier PDF signé
validateSignature.submit=Valider les signatures
validateSignature.results=Résultats de la validation
validateSignature.status=Statut
validateSignature.signer=Signataire
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.reason=Raison
validateSignature.location=Localisation
validateSignature.noSignatures=Aucune signature numérique trouvée dans ce document
validateSignature.status.valid=Valide
validateSignature.status.invalid=Invalide
validateSignature.chain.invalid=La validation de la chaîne de certificats a échoué - impossible de vérifier l'identité du signataire
validateSignature.trust.invalid=Le certificat n'est pas dans le magasin de confiance - la source ne peut pas être vérifiée
validateSignature.cert.expired=Le certificat a expiré
validateSignature.cert.revoked=Le certificat a été révoqué
validateSignature.signature.info=Informations sur la signature
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.signature.mathValid=La signature est mathématiquement valide MAIS :
validateSignature.selectCustomCert=Fichier de certificat personnalisé X.509 (Optionnel)
validateSignature.cert.info=Détails du certificat
validateSignature.cert.issuer=Émetteur
validateSignature.cert.subject=Sujet
validateSignature.cert.serialNumber=Numéro de série
validateSignature.cert.validFrom=Valide à partir du
validateSignature.cert.validUntil=Valide jusqu'au
validateSignature.cert.algorithm=Algorithme
validateSignature.cert.keySize=Taille de la clé
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.keyUsage=Usage de la clé
validateSignature.cert.selfSigned=Auto-signé
validateSignature.cert.bits=bits

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Iarratas Neamhbhailí
login.oauth2AccessDenied=Rochtain Diúltaithe
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Deisiúchán
repair.header=PDF a dheisiú
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Scála Liath
pdfToImage.blackwhite=Dubh agus Bán (Dfhéadfadh sonraí a chailleadh!)
pdfToImage.submit=Tiontaigh
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(m.sh. 1,2,8 nó 4,7,12-16 nó 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=गलत याचना
login.oauth2AccessDenied=इनपुट उम्मीदवार डिसकार
login.oauth2InvalidTokenResponse=अमान्तरित सिक्वेंस जवाब कैसे है
login.oauth2InvalidIdToken=गलत इड टोकन
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=उपयोगकर्ता डिसबाल, यह वर्षा सभी उपयोगकर्ता जूझे वर्षाकरण बारा मिल गई है। कृपया संबवादक से संपर्क करें.
login.alreadyLoggedIn=आप पहले से ही
login.alreadyLoggedIn2=सुनिश्चित करने वाले डिवाइस्स पर लॉग-इन हैं। कृपया इन डिवाइस से लॉगआउट करें और पुनः प्रयास करें
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=मरम्मत
repair.header=पीडीएफ़ मरम्मत करें
@@ -1041,6 +1042,7 @@ pdfToImage.grey=ग्रे स्केल
pdfToImage.blackwhite=काला और सफेद (डेटा खो सकता है!)
pdfToImage.submit=परिवर्तित करें
pdfToImage.info=पायथन नहीं अनिस्तारित है। वेबP परिवर्तन के लिए आवश्यक है।
pdfToImage.placeholder=(उदाहरण के लिए 1,2,8 या 4,7,12-16 या 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF विभाजित
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Neispravan zahtjev
login.oauth2AccessDenied=Pristup odbijen
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
login.oauth2InvalidIdToken=Nevažeći ID token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Korisnik je deaktiviran, prijava sa ovim korisničkim imenom je trenutno zakazana. Molimo Vas da kontaktirate administratorske osobe.
login.alreadyLoggedIn=Već ste se prijavili na
login.alreadyLoggedIn2=ure. Odjavite se s ure i pokušajte ponovo.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Popravi
repair.header=Popravi PDF datoteku
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Sivi tonovi
pdfToImage.blackwhite=Crno-bijelo (mogu se izgubiti podaci!)
pdfToImage.submit=Pretvori
pdfToImage.info=Python nije instaliran. Treba je za konverziju na WebP.
pdfToImage.placeholder=(t.j. 1,2,8 ili 4,7,12-16 ili 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Podijeli PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Érvénytelen kérelem
login.oauth2AccessDenied=Hozzáférés megtagadva
login.oauth2InvalidTokenResponse=Érvénytelen token-válasz
login.oauth2InvalidIdToken=Érvénytelen azonosító token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=A felhasználó deaktivált, a bejelentkezés jelenleg megszakítva ezzel a felhasználónévvel. Kérjen segítséget a rendszergazdától.
login.alreadyLoggedIn=Már be van jelentkezve az
login.alreadyLoggedIn2=eszközökre. Kijelentkezzen ezekből a eszközökből, majd próbálja újra bejelentkezni.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Javítás
repair.header=PDF-ek javítása
@@ -1041,6 +1042,7 @@ pdfToImage.grey=szürkeárnyalatos
pdfToImage.blackwhite=fekete-fehér (adatvesztéssel járhat!)
pdfToImage.submit=Átalakítás
pdfToImage.info=Nincs telepítve a Python. Szükséges a WebP konverzióhoz.
pdfToImage.placeholder=(pl. 1,2,8 vagy 4,7,12-16 vagy 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF osztás
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Permintaan Tidak Valid
login.oauth2AccessDenied=Akses Ditolak
login.oauth2InvalidTokenResponse=Respons Token Tidak Valid
login.oauth2InvalidIdToken=Token ID Tidak Valid
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Pengguna dinonaktifkan, login saat ini diblokir dengan nama pengguna ini. Silakan hubungi administrator.
login.alreadyLoggedIn=Anda sudah login ke
login.alreadyLoggedIn2=perangkat. Silakan keluar dari perangkat dan coba lagi.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Perbaiki
repair.header=Perbaiki PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Skala abu-abu
pdfToImage.blackwhite=Black and White (Bisa kehilangan data!)
pdfToImage.submit=Konversi
pdfToImage.info=Python tidak terinstal. Diperlukan untuk konversi WebP.
pdfToImage.placeholder=(misalnya 1,2,8 atau 4,7,12-16 atau 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Pecah PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Richiesta non valida
login.oauth2AccessDenied=Accesso negato
login.oauth2InvalidTokenResponse=Risposta token non valida
login.oauth2InvalidIdToken=Id Token non valido
login.relyingPartyRegistrationNotFound=Nessuna registrazione di parte affidabile trovata
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
login.alreadyLoggedIn=Hai già effettuato l'accesso a
login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova.
@@ -830,7 +831,7 @@ sign.first=Prima pagina
sign.last=Ultima pagina
sign.next=Prossima pagina
sign.previous=Pagina precedente
sign.maintainRatio=Attiva il mantenimento delle proporzioni
#repair
repair.title=Ripara
repair.header=Ripara PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Scala di grigi
pdfToImage.blackwhite=Bianco e Nero (potresti perdere dettagli!)
pdfToImage.submit=Converti
pdfToImage.info=Python non è installato.È richiesto per la conversione WebP.
pdfToImage.placeholder=(es. 1,2,8 o 4,7,12-16 o 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividi PDF
fileChooser.click=Clicca
fileChooser.or=o
fileChooser.dragAndDrop=Trascina & Rilascia
fileChooser.dragAndDropPDF=Trascina & rilascia il file PDF
fileChooser.dragAndDropImage=Trascina & rilascia il file immagine
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=無効なリクエスト
login.oauth2AccessDenied=アクセス拒否
login.oauth2InvalidTokenResponse=無効なトークン応答
login.oauth2InvalidIdToken=無効なIDトークン
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
login.alreadyLoggedIn=すでにログインしています
login.alreadyLoggedIn2=デバイスからログアウトしてもう一度お試しください。
@@ -830,7 +831,7 @@ sign.first=最初のページ
sign.last=最後のページ
sign.next=次のページ
sign.previous=前のページ
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=修復
repair.header=PDFを修復
@@ -1041,6 +1042,7 @@ pdfToImage.grey=グレースケール
pdfToImage.blackwhite=白黒 (データが失われる可能性があります!)
pdfToImage.submit=変換
pdfToImage.info=Pythonがインストールされていません。WebPの変換に必要です。
pdfToImage.placeholder=(例:1,2,8、4,7,12-16、2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDFを分割
fileChooser.click=クリック
fileChooser.or=または
fileChooser.dragAndDrop=ドラッグ&ドロップ
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=복구
repair.header=PDF 복구
@@ -1041,6 +1042,7 @@ pdfToImage.grey=그레이스케일
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
pdfToImage.submit=변환
pdfToImage.info=Python이 설치되어 있지 않습니다. WebP 변환에 필요합니다.
pdfToImage.placeholder=(예: 1,2,8 또는 4,7,12-16 또는 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF 분할
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ongeldig verzoek
login.oauth2AccessDenied=Toegang geweigerd
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
login.oauth2InvalidIdToken=Ongeldige ID token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=De gebruiker is gedesactiveerd, inloggen is momenteel geblokkeerd voor deze gebruikersnaam. Neem contact op met de beheerder.
login.alreadyLoggedIn=U zit reeds ingelogd bij
login.alreadyLoggedIn2=apparaten. U moet u a.u.b. uitloggen van de apparaten en opnieuw proberen.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Repareren
repair.header=PDF's repareren
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Grijstinten
pdfToImage.blackwhite=Zwart en wit (kan data verliezen!)
pdfToImage.submit=Omzetten
pdfToImage.info=Python is niet geïnstalleerd. Vereist voor WebP-conversie.
pdfToImage.placeholder=(bijv. 1,2,8 of 4,7,12-16 of 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF splitsen
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ugyldig forespørsel
login.oauth2AccessDenied=Tilgang nektet
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
login.oauth2InvalidIdToken=Ugyldig Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparer
repair.header=Reparer PDF-er
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gråtone
pdfToImage.blackwhite=Svart-hvitt (kan miste data!)
pdfToImage.submit=Konverter
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(f.eks. 1,2,8 eller 4,7,12-16 eller 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Nieprawidłowe żądanie
login.oauth2AccessDenied=Brak dostępu
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Użytkownik jest nieaktywny, logowanie przy użyciu tej nazwy użytkownika jest obecnie zablokowane. Prosimy o kontakt z administratorem.
login.alreadyLoggedIn=Jesteś już zalogowany na
login.alreadyLoggedIn2=urządzeniach. Wyloguj się z tych urządzeń i spróbuj ponownie.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Napraw
repair.header=Napraw dokument(y) PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Odcień szarości
pdfToImage.blackwhite=Czarno-biały (może spowodować utratę danych!)
pdfToImage.submit=Konwertuj
pdfToImage.info=Python nie został zainstalowany. Jest wymagany do konwersji WebP.
pdfToImage.placeholder=(przykład 1,2,8 lub 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Podziel PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requisição Inválida
login.oauth2AccessDenied=Acesso Negado
login.oauth2InvalidTokenResponse=Resposta de Token Inválida
login.oauth2InvalidIdToken=Id de Token Inválido
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=O usuário está desativado, o login está atualmente bloqueado com este nome de usuário. Entre em contato com o administrador.
login.alreadyLoggedIn=Você já está conectado em
login.alreadyLoggedIn2=aparelhos. Por favor saia dos aparelhos e tente novamente.
@@ -830,7 +831,7 @@ sign.first=Primeira página
sign.last=Última página
sign.next=Próxima página
sign.previous=Página anterior
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparar
repair.header=Reparar
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de Cinza
pdfToImage.blackwhite=Preto e Branco (pode perder informações!)
pdfToImage.submit=Converter
pdfToImage.info=Python não está instalado. Necessário para conversão WebP.
pdfToImage.placeholder=(por exemplo 1,2,8 or 4,7,12-16 ou 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividir
fileChooser.click=Clique
fileChooser.or=ou
fileChooser.dragAndDrop=Arraste & Solte
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Arraste & Solte arquivo(s) aqui
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requisito inválido
login.oauth2AccessDenied=Acesso negado
login.oauth2InvalidTokenResponse=Resposta de token inválida
login.oauth2InvalidIdToken=Token de identificação inválido
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=O utilizador foi desativado, o login está atualmente bloqueado com esta conta. Por favor, contacte o administrador.
login.alreadyLoggedIn=Já está logado em
login.alreadyLoggedIn2=dispositivos. Por favor, faça logout nos dispositivos e tente novamente.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparar
repair.header=Reparar PDFs
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de Cinza
pdfToImage.blackwhite=Preto e Branco (pode resultar em perda de dados!)
pdfToImage.submit=Converter
pdfToImage.info=O Python não está instalado. Necessário para a conversão de WebP.
pdfToImage.placeholder=(ex: 1,2,8 ou 4,7,12-16 ou 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividir o PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Cerere Invalidă
login.oauth2AccessDenied=Acces Refuzat
login.oauth2InvalidTokenResponse=Răspuns Invalid la Token
login.oauth2InvalidIdToken=Token de Id Invalid
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Utilizatorul este dezactivat, conectarea este în prezent blocată cu acest nume de utilizator. Te rugăm să contactezi administratorul.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Repară
repair.header=Repară documente PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Scală de gri
pdfToImage.blackwhite=Alb și negru (Poate pierde date!)
pdfToImage.submit=Convertește
pdfToImage.info=Python nu este instalat. Necesar pentru conversia WebP.
pdfToImage.placeholder=(ex. 1,2,8 sau 4,7,12-16 sau 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Неверный запрос
login.oauth2AccessDenied=Доступ запрещен
login.oauth2InvalidTokenResponse=Недействительный ответ токена
login.oauth2InvalidIdToken=Недействительный идентификационный токен
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Пользователь деактивирован, вход с данным именем пользователя заблокирован. Пожалуйста, обратитесь к администратору.
login.alreadyLoggedIn=Вы уже вошли в
login.alreadyLoggedIn2=устройства. Выполните выход из устройств и попробуйте снова.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Ремонт
repair.header=Ремонт PDF ов
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Оттенки серого
pdfToImage.blackwhite=Черно-белый (может потерять данные!)
pdfToImage.submit=Конвертировать
pdfToImage.info=Питон не установлен. Необходим для конвертации в WebP.
pdfToImage.placeholder=(например 1,2,8 или 4,7,12-16 или 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Разделить PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Opraviť
repair.header=Opraviť PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Odtiene šedej
pdfToImage.blackwhite=Čierno-biele (Môže stratiť údaje!)
pdfToImage.submit=Konvertovať
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(napr. 1,2,8 alebo 4,7,12-16 alebo 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Popravi
repair.header=Popravi PDF fajlove
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Nijanse sive
pdfToImage.blackwhite=Crno-belo (Može izgubiti podatke!)
pdfToImage.submit=Konvertuj
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -250,7 +250,7 @@ database.fileNullOrEmpty=Filen får inte vara null eller tom
database.failedImportFile=Misslyckades med att importera fil
session.expired=Din session har löpt ut. Uppdatera sidan och försök igen.
session.refreshPage=Refresh Page
session.refreshPage=Uppdatera sida
#############
# HOME-PAGE #
@@ -519,9 +519,9 @@ home.validateSignature.desc=Verify digital signatures and certificates in PDF do
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.title=Ersätt-Invertera-Färg
replace-color.header=Ersätt-Invertera färg på PDF
home.replaceColorPdf.title=Replace and Invert Color
home.replaceColorPdf.title=Ersätt och Invertera färg
home.replaceColorPdf.desc=Ersätt färg för text och bakgrund i PDF och invertera hela färgen på PDF för att minska filstorlek
replaceColorPdf.tags=Ersätt Färg, Sidåtgärder, Bakomliggande, Serversid
replace-color.selectText.1=Ersätt eller Invertera färgalternativ
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ogiltig begäran
login.oauth2AccessDenied=Åtkomst nekad
login.oauth2InvalidTokenResponse=Ogiltigt token-svar
login.oauth2InvalidIdToken=Ogiltigt Id-token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Användaren är inaktiverad, inloggning är för närvarande blockerad med detta användarnamn. Kontakta administratören.
login.alreadyLoggedIn=Du är redan inloggad på
login.alreadyLoggedIn2=enheter. Logga ut från enheterna och försök igen.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Reparera
repair.header=Reparera PDF-filer
@@ -968,17 +969,17 @@ multiTool.undo=Undo
multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
decrypt.passwordPrompt=Denna fil är lösenordsskyddad. Fyll i lösenord:
decrypt.cancelled=Operation misslyckades för PDF: {0}
decrypt.noPassword=Inget lösenord angivet för krypterad PDF: {0}
decrypt.invalidPassword=Försök igen med korrekt lösenord.
decrypt.invalidPasswordHeader=Felaktigt lösenord eller osupportad kryptering för PDF: {0}
decrypt.unexpectedError=Det uppstod ett fel vid processering av filen. Vänligen försök igen.
decrypt.serverError=Serverfel vid avkryptering: {0}
decrypt.success=Fil avkrypterad.
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
multiTool-advert.message=Denna funktion finns också tillgänglig i vår <a href="{0}">multi-tool page</a>. Spana in den för bättre sida-för-sida anpassning och ytterligare funktioner!
#view pdf
viewPdf.title=Visa PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gråskala
pdfToImage.blackwhite=Svartvitt (kan förlora data!)
pdfToImage.submit=Konvertera
pdfToImage.info=Python är inte installerat. Krävs för WebP-konvertering.
pdfToImage.placeholder=(t.ex. 1,2,8 eller 4,7,12-16 eller 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dela upp PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=คำขอไม่ถูกต้อง
login.oauth2AccessDenied=การเข้าถึงถูกปฏิเสธ
login.oauth2InvalidTokenResponse=การตอบกลับโทเค็นไม่ถูกต้อง
login.oauth2InvalidIdToken=โทเค็น Id ไม่ถูกต้อง
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=ผู้ใช้งานถูกระงับการใช้งาน ไม่สามารถเข้าสู่ระบบด้วยชื่อผู้ใช้นี้ได้ กรุณาติดต่อผู้ดูแลระบบ
login.alreadyLoggedIn=คุณได้เข้าสู่ระบบใน
login.alreadyLoggedIn2=อุปกรณ์แล้ว กรุณาออกจากระบบจากอุปกรณ์ที่ใช้งานอยู่แล้ว จากนั้นลองใหม่อีกครั้ง
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=ซ่อมแซม
repair.header=ซ่อมแซม PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=ระดับสีเทา
pdfToImage.blackwhite=ขาวดำ (อาจสูญเสียข้อมูล!)
pdfToImage.submit=แปลง
pdfToImage.info=Python ไม่มีการติดตั้ง จำเป็นสำหรับการแปลง WebP
pdfToImage.placeholder=(เช่น 1,2,8 หรือ 4,7,12-16 หรือ 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=แบ่งไฟล์ PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Geçersiz İstek
login.oauth2AccessDenied=Erişim Reddedildi
login.oauth2InvalidTokenResponse=Geçersiz Belirteç Yanıtı
login.oauth2InvalidIdToken=Geçersiz Kimlik Belirteci
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=Kullanıcı devre dışı bırakıldı, şu anda bu kullanıcı adıyla giriş engellendi. Lütfen yöneticiyle iletişime geçin.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Onar
repair.header=PDF'leri Onar
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gri tonlama
pdfToImage.blackwhite=Siyah ve Beyaz (Veri kaybolabilir!)
pdfToImage.submit=Dönüştür
pdfToImage.info=Python kurulu değil. WebP dönüşümü için gereklidir.
pdfToImage.placeholder=(örneğin 1,2,8 veya 4,7,12-16 ya da 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Недійсний запит
login.oauth2AccessDenied=Доступ заблоковано
login.oauth2InvalidTokenResponse=Недійсна відповідь з токеном
login.oauth2InvalidIdToken=Недійсний Id токен
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Ремонт
repair.header=Ремонт PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Відтінки сірого
pdfToImage.blackwhite=Чорно-білий (може втратити дані!)
pdfToImage.submit=Конвертувати
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(наприклад 1,2,8 або 4,7,12-16 або 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Yêu cầu không hợp lệ
login.oauth2AccessDenied=Truy cập bị từ chối
login.oauth2InvalidTokenResponse=Phản hồi token không hợp lệ
login.oauth2InvalidIdToken=Id Token không hợp lệ
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
@@ -830,7 +831,7 @@ sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=Sửa chữa
repair.header=Sửa chữa PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Thang độ xám
pdfToImage.blackwhite=Đen trắng (Có thể mất dữ liệu!)
pdfToImage.submit=Chuyển đổi
pdfToImage.info=Python is not installed. Required for WebP conversion.
pdfToImage.placeholder=(ví dụ: 1,2,8 hoặc 4,7,12-16 hoặc 2n-1)
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=无效请求
login.oauth2AccessDenied=拒绝访问
login.oauth2InvalidTokenResponse=无效的 Token 响应
login.oauth2InvalidIdToken=无效的 Token
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=用户被禁用,登录已被阻止。请联系管理员。
login.alreadyLoggedIn=您已经登录到了
login.alreadyLoggedIn2=设备,请注销设备后重试。
@@ -830,7 +831,7 @@ sign.first=首页
sign.last=末页
sign.next=下一页
sign.previous=上一页
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=修复
repair.header=修复 PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=灰度
pdfToImage.blackwhite=黑白(可能会丢失数据!)。
pdfToImage.submit=转换
pdfToImage.info=WebP 转换需要安装 Python
pdfToImage.placeholder=例如1,2,8 或 4,7,12-16 或 2n-1
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=拆分 PDF
fileChooser.click=单击
fileChooser.or=
fileChooser.dragAndDrop=拖放文件
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=拖放文件到此处
#release notes

View File

@@ -561,6 +561,7 @@ login.oauth2invalidRequest=無效的回應
login.oauth2AccessDenied=存取被拒
login.oauth2InvalidTokenResponse=無效的權杖回應
login.oauth2InvalidIdToken=無效的識別權杖
login.relyingPartyRegistrationNotFound=No relying party registration found
login.userIsDisabled=使用者已停用,目前此使用者無法登入。請聯絡系統管理員。
login.alreadyLoggedIn=您已經登入了
login.alreadyLoggedIn2=個裝置。請登出其他裝置後再試一次。
@@ -830,7 +831,7 @@ sign.first=第一頁
sign.last=最後一頁
sign.next=下一頁
sign.previous=上一頁
sign.maintainRatio=Toggle maintain aspect ratio
#repair
repair.title=修復
repair.header=修復 PDF
@@ -1041,6 +1042,7 @@ pdfToImage.grey=灰度
pdfToImage.blackwhite=黑白(可能會遺失資料!)
pdfToImage.submit=轉換
pdfToImage.info=尚未安裝 Python。需要安裝 Python 才能進行 WebP 轉換。
pdfToImage.placeholder=(例如 1,2,8 或 4,7,12-16 或 2n-1
#addPassword
@@ -1284,6 +1286,8 @@ splitByChapters.submit=分割 PDF
fileChooser.click=點選
fileChooser.or=
fileChooser.dragAndDrop=拖放檔案
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=將檔案拖放至此
#release notes

View File

@@ -84,27 +84,6 @@
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base",
"moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-base",
"moduleVersion": "2.18.2",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider",
"moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-json-provider",
"moduleVersion": "2.18.2",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.module:jackson-module-jaxb-annotations",
"moduleUrl": "https://github.com/FasterXML/jackson-modules-base",
"moduleVersion": "2.18.2",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names",
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names",
@@ -362,20 +341,6 @@
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.unboundid.product.scim2:scim2-sdk-client",
"moduleUrl": "https://github.com/pingidentity/scim2",
"moduleVersion": "2.3.5",
"moduleLicense": "UnboundID SCIM2 SDK Free Use License",
"moduleLicenseUrl": "https://github.com/pingidentity/scim2"
},
{
"moduleName": "com.unboundid.product.scim2:scim2-sdk-common",
"moduleUrl": "https://github.com/pingidentity/scim2",
"moduleVersion": "2.3.5",
"moduleLicense": "UnboundID SCIM2 SDK Free Use License",
"moduleLicenseUrl": "https://github.com/pingidentity/scim2"
},
{
"moduleName": "com.zaxxer:HikariCP",
"moduleUrl": "https://github.com/brettwooldridge/HikariCP",
@@ -584,20 +549,6 @@
"moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
"moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
},
{
"moduleName": "javax.activation:javax.activation-api",
"moduleUrl": "http://www.oracle.com",
"moduleVersion": "1.2.0",
"moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0",
"moduleLicenseUrl": "https://opensource.org/licenses/CDDL-1.0"
},
{
"moduleName": "javax.xml.bind:jaxb-api",
"moduleUrl": "http://www.oracle.com/",
"moduleVersion": "2.3.1",
"moduleLicense": "GPL2 w/ CPE",
"moduleLicenseUrl": "https://oss.oracle.com/licenses/CDDL+GPL-1.1"
},
{
"moduleName": "me.friwi:gluegen-rt",
"moduleUrl": "http://jogamp.org/gluegen/www/",

View File

@@ -2,22 +2,32 @@
position: relative;
margin: 20px 0;
}
#pdf-canvas {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
width: 100%;
}
.draggable-buttons-box {
position: absolute;
position: relative;
top: 0;
padding: 10px;
width: 100%;
width: calc(100% + 4.4rem);
display: flex;
gap: 5px;
z-index: 5;
margin-left: -2.2rem;
}
.draggable-buttons-box > button {
z-index: 10;
.draggable-buttons-box>button {
z-index: 4;
background-color: rgba(13, 110, 253, 0.1);
flex: 1 1 auto;
min-width: 2.5rem;
max-width: 4rem;
}
.draggable-canvas {
border: 1px solid red;
position: absolute;
@@ -26,3 +36,20 @@
top: 0px;
left: 0;
}
.input-with-icon {
position: relative;
display: inline-flex;
align-items: center;
}
.input-with-icon .icon {
position: absolute;
left: 0.5rem;
pointer-events: none;
color: #aaa;
}
.input-with-icon input {
padding-left: 2.2rem;
}

View File

@@ -118,6 +118,7 @@
row-gap: 1px;
height: 60px;
width: 60px;
top:4px;
}
.file-icon {
@@ -165,8 +166,8 @@
height: 15px;
width: 15px;
right: 10px;
top: -5px;
right: 0px;
top: -17px;
}
.remove-selected-file * {
@@ -219,3 +220,54 @@
border-radius: 1rem;
border: 1px solid rgb(105, 116, 134, 0.5);
}
.draggable-image-overlay{
position: absolute;
background: rgba(0, 0, 0, 0.7);
display: none;
z-index: 10;
align-items: center;
justify-content: center;
color: white;
font-size: 16px;
font-weight: bold;
pointer-events: none;
left:0;
top:0;
height:100%;
width:100%;
border-radius: 1rem;
}
.small-file-container:hover .drag-icon {
display: flex;
}
.drag-icon {
display: none;
position: absolute;
top: 5px;
width: 20px;
height: 20px;
background: rgba(0, 0, 0, 0.5);
color: white;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 14px;
pointer-events: none;
z-index: 1;
}
#imagePreviewModal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
display: none;
justify-content: center;
align-items: center;
z-index: 9999;
}

View File

@@ -22,18 +22,38 @@ select#font-select option {
}
.draggable-buttons-box {
position: absolute;
position: relative;
top: 0;
padding: 10px;
width: 100%;
width: calc(100% + 4.4rem);
display: flex;
gap: 5px;
z-index: 5;
margin-left: -2.2rem;
}
.draggable-buttons-box>button {
z-index: 4;
background-color: rgba(13, 110, 253, 0.1);
flex: 1 1 auto;
min-width: 2.5rem;
max-width: 4rem;
}
.rotation-handle {
width: 20px;
height: 20px;
border: 2px solid #3498db;
background-color: rgba(52, 152, 219, 0.1);
color: white;
border-radius: 50%;
text-align: center;
line-height: 20px;
position: absolute;
cursor: grab;
top: -30px;
left: calc(50% - 10px);
}
.draggable-canvas {
@@ -113,3 +133,33 @@ select#font-select option {
text-align: right;
padding: 0.5rem 1rem;
}
.input-with-icon {
position: relative;
display: inline-flex;
align-items: center;
}
.input-with-icon .icon {
position: absolute;
left: 0.5rem;
pointer-events: none;
color: #aaa;
}
.input-with-icon input {
padding-left: 2.2rem;
}
.small-file-container-saved {
padding-top: 1px;
position: relative;
row-gap: 1px;
height: 60px;
width: 60px;
top: 4px;
}
.small-file-container-saved:hover .drag-icon {
display: flex;
}

View File

@@ -7,75 +7,137 @@ const DraggableUtils = {
elementAllPages: [],
documentsMap: new Map(),
lastInteracted: null,
padding: 15,
maintainRatioEnabled: true,
init() {
interact('.draggable-canvas')
.draggable({
listeners: {
start(event) {
const target = event.target;
x = parseFloat(target.getAttribute('data-bs-x'));
y = parseFloat(target.getAttribute('data-bs-y'));
},
move: (event) => {
const target = event.target;
const x = (parseFloat(target.getAttribute('data-bs-x')) || 0) + event.dx;
const y = (parseFloat(target.getAttribute('data-bs-y')) || 0) + event.dy;
// Retrieve position attributes
let x = parseFloat(target.getAttribute('data-bs-x')) || 0;
let y = parseFloat(target.getAttribute('data-bs-y')) || 0;
const angle = parseFloat(target.getAttribute('data-angle')) || 0;
// Update position based on drag movement
x += event.dx;
y += event.dy;
// Apply translation to the parent container (bounding box)
target.style.transform = `translate(${x}px, ${y}px)`;
// Preserve rotation on the inner canvas
const canvas = target.querySelector('.display-canvas');
const canvasWidth = parseFloat(canvas.style.width);
const canvasHeight = parseFloat(canvas.style.height);
const cosAngle = Math.abs(Math.cos(angle));
const sinAngle = Math.abs(Math.sin(angle));
const rotatedWidth = canvasWidth * cosAngle + canvasHeight * sinAngle;
const rotatedHeight = canvasWidth * sinAngle + canvasHeight * cosAngle;
const offsetX = (rotatedWidth - canvasWidth) / 2;
const offsetY = (rotatedHeight - canvasHeight) / 2;
canvas.style.transform = `translate(${offsetX}px, ${offsetY}px) rotate(${angle}rad)`;
// Update attributes for persistence
target.setAttribute('data-bs-x', x);
target.setAttribute('data-bs-y', y);
this.onInteraction(target);
//update the last interacted element
this.lastInteracted = event.target;
// Set the last interacted element
this.lastInteracted = target;
},
},
})
.resizable({
edges: {left: true, right: true, bottom: true, top: true},
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
start: (event) => {
const target = event.target;
x = parseFloat(target.getAttribute('data-bs-x')) || 0;
y = parseFloat(target.getAttribute('data-bs-y')) || 0;
},
move: (event) => {
var target = event.target;
var x = parseFloat(target.getAttribute('data-bs-x')) || 0;
var y = parseFloat(target.getAttribute('data-bs-y')) || 0;
const target = event.target;
// check if control key is pressed
if (event.ctrlKey) {
const aspectRatio = target.offsetWidth / target.offsetHeight;
// preserve aspect ratio
let width = event.rect.width;
let height = event.rect.height;
const MAX_CHANGE = 60;
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
height = width / aspectRatio;
} else {
width = height * aspectRatio;
let width = event.rect.width - 2 * this.padding;
let height = event.rect.height - 2 * this.padding;
const canvas = target.querySelector('.display-canvas');
if (canvas) {
const originalWidth = parseFloat(canvas.style.width) || canvas.width;
const originalHeight = parseFloat(canvas.style.height) || canvas.height;
const angle = parseFloat(target.getAttribute('data-angle')) || 0;
const aspectRatio = originalWidth / originalHeight;
if (!event.ctrlKey && this.maintainRatioEnabled) {
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
height = width / aspectRatio;
} else {
width = height * aspectRatio;
}
}
event.rect.width = width;
event.rect.height = height;
const widthChange = width - originalWidth;
const heightChange = height - originalHeight;
if (Math.abs(widthChange) > MAX_CHANGE || Math.abs(heightChange) > MAX_CHANGE) {
const scale = MAX_CHANGE / Math.max(Math.abs(widthChange), Math.abs(heightChange));
width = originalWidth + widthChange * scale;
height = originalHeight + heightChange * scale;
}
const cosAngle = Math.abs(Math.cos(angle));
const sinAngle = Math.abs(Math.sin(angle));
const boundingWidth = width * cosAngle + height * sinAngle;
const boundingHeight = width * sinAngle + height * cosAngle;
if (event.edges.left) {
const dx = event.deltaRect.left;
x += dx;
}
if (event.edges.top) {
const dy = event.deltaRect.top;
y += dy;
}
target.style.transform = `translate(${x}px, ${y}px)`;
target.style.width = `${boundingWidth + 2 * this.padding}px`;
target.style.height = `${boundingHeight + 2 * this.padding}px`;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
canvas.style.transform = `translate(${(boundingWidth - width) / 2}px, ${(boundingHeight - height) / 2
}px) rotate(${angle}rad)`;
target.setAttribute('data-bs-x', x);
target.setAttribute('data-bs-y', y);
this.lastInteracted = target;
}
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.transform = 'translate(' + x + 'px,' + y + 'px)';
target.setAttribute('data-bs-x', x);
target.setAttribute('data-bs-y', y);
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height);
this.onInteraction(target);
},
},
modifiers: [
interact.modifiers.restrictSize({
min: {width: 5, height: 5},
min: { width: 50, height: 50 },
}),
],
inertia: true,
});
//Arrow key Support for Add-Image and Sign pages
if (window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) {
window.addEventListener('keydown', (event) => {
@@ -117,7 +179,8 @@ const DraggableUtils = {
}
// Update position
target.style.transform = `translate(${x}px, ${y}px)`;
const angle = parseFloat(target.getAttribute('data-angle')) || 0;
target.style.transform = `translate(${x}px, ${y}px) rotate(${angle}rad)`;
target.setAttribute('data-bs-x', x);
target.setAttribute('data-bs-y', y);
@@ -125,72 +188,97 @@ const DraggableUtils = {
});
}
},
onInteraction(target) {
this.boxDragContainer.appendChild(target);
},
createDraggableCanvas() {
const createdCanvas = document.createElement('canvas');
createdCanvas.id = `draggable-canvas-${this.nextId++}`;
createdCanvas.classList.add('draggable-canvas');
const x = 0;
const y = 20;
createdCanvas.style.transform = `translate(${x}px, ${y}px)`;
createdCanvas.setAttribute('data-bs-x', x);
createdCanvas.setAttribute('data-bs-y', y);
//Click element in order to enable arrow keys
createdCanvas.addEventListener('click', () => {
this.lastInteracted = createdCanvas;
});
createdCanvas.onclick = (e) => this.onInteraction(e.target);
this.boxDragContainer.appendChild(createdCanvas);
//Enable Arrow keys directly after the element is created
this.lastInteracted = createdCanvas;
return createdCanvas;
this.lastInteracted = target;
// this.boxDragContainer.appendChild(target);
// target.appendChild(target.querySelector(".display-canvas"));
},
createDraggableCanvasFromUrl(dataUrl) {
return new Promise((resolve) => {
var myImage = new Image();
const canvasContainer = document.createElement('div');
const createdCanvas = document.createElement('canvas'); // Inner canvas
const padding = this.padding;
canvasContainer.id = `draggable-canvas-${this.nextId++}`;
canvasContainer.classList.add('draggable-canvas');
createdCanvas.classList.add('display-canvas');
canvasContainer.style.position = 'absolute';
canvasContainer.style.padding = `${padding}px`;
canvasContainer.style.overflow = 'hidden';
let x = 0,
y = 30,
angle = 0;
canvasContainer.style.transform = `translate(${x}px, ${y}px)`;
canvasContainer.setAttribute('data-bs-x', x);
canvasContainer.setAttribute('data-bs-y', y);
canvasContainer.setAttribute('data-angle', angle);
canvasContainer.addEventListener('click', () => {
this.lastInteracted = canvasContainer;
this.showRotationControls(canvasContainer);
});
canvasContainer.appendChild(createdCanvas);
this.boxDragContainer.appendChild(canvasContainer);
const myImage = new Image();
myImage.src = dataUrl;
myImage.onload = () => {
var createdCanvas = this.createDraggableCanvas();
const context = createdCanvas.getContext('2d');
createdCanvas.width = myImage.width;
createdCanvas.height = myImage.height;
const imgAspect = myImage.width / myImage.height;
const pdfAspect = this.boxDragContainer.offsetWidth / this.boxDragContainer.offsetHeight;
const containerWidth = this.boxDragContainer.offsetWidth;
const containerHeight = this.boxDragContainer.offsetHeight;
var scaleMultiplier;
if (imgAspect > pdfAspect) {
scaleMultiplier = this.boxDragContainer.offsetWidth / myImage.width;
} else {
scaleMultiplier = this.boxDragContainer.offsetHeight / myImage.height;
}
let scaleMultiplier = Math.min(containerWidth / myImage.width, containerHeight / myImage.height);
const scaleFactor = 0.5;
var newWidth = createdCanvas.width;
var newHeight = createdCanvas.height;
if (scaleMultiplier < 1) {
newWidth = newWidth * scaleMultiplier;
newHeight = newHeight * scaleMultiplier;
}
const newWidth = myImage.width * scaleMultiplier * scaleFactor;
const newHeight = myImage.height * scaleMultiplier * scaleFactor;
createdCanvas.style.width = newWidth + 'px';
createdCanvas.style.height = newHeight + 'px';
// Calculate initial bounding box size
const cosAngle = Math.abs(Math.cos(angle));
const sinAngle = Math.abs(Math.sin(angle));
const boundingWidth = newWidth * cosAngle + newHeight * sinAngle;
const boundingHeight = newWidth * sinAngle + newHeight * cosAngle;
var myContext = createdCanvas.getContext('2d');
myContext.drawImage(myImage, 0, 0);
resolve(createdCanvas);
createdCanvas.style.width = `${newWidth}px`;
createdCanvas.style.height = `${newHeight}px`;
canvasContainer.style.width = `${boundingWidth + 2 * padding}px`;
canvasContainer.style.height = `${boundingHeight + 2 * padding}px`;
context.imageSmoothingEnabled = true;
context.imageSmoothingQuality = 'high';
context.drawImage(myImage, 0, 0, myImage.width, myImage.height);
this.showRotationControls(canvasContainer);
this.lastInteracted = canvasContainer;
resolve(canvasContainer);
};
myImage.onerror = () => {
console.error('Failed to load the image.');
resolve(null);
};
});
},
toggleMaintainRatio() {
this.maintainRatioEnabled = !this.maintainRatioEnabled;
const button = document.getElementById('ratioToggleBtn');
if (this.maintainRatioEnabled) {
button.classList.remove('btn-danger');
button.classList.add('btn-outline-secondary');
} else {
button.classList.remove('btn-outline-secondary');
button.classList.add('btn-danger');
}
},
deleteAllDraggableCanvases() {
this.boxDragContainer.querySelectorAll('.draggable-canvas').forEach((el) => el.remove());
},
@@ -266,9 +354,61 @@ const DraggableUtils = {
}
},
getLastInteracted() {
return this.boxDragContainer.querySelector('.draggable-canvas:last-of-type');
return this.lastInteracted;
},
showRotationControls(element) {
const rotationControls = document.getElementById('rotation-controls');
const rotationInput = document.getElementById('rotation-input');
rotationControls.style.display = 'flex';
rotationInput.value = Math.round((parseFloat(element.getAttribute('data-angle')) * 180) / Math.PI);
rotationInput.addEventListener('input', this.handleRotationInputChange);
},
hideRotationControls() {
const rotationControls = document.getElementById('rotation-controls');
const rotationInput = document.getElementById('rotation-input');
rotationControls.style.display = 'none';
rotationInput.addEventListener('input', this.handleRotationInputChange);
},
applyRotationToElement(element, degrees) {
const radians = degrees * (Math.PI / 180); // Convert degrees to radians
// Get current position
const x = parseFloat(element.getAttribute('data-bs-x')) || 0;
const y = parseFloat(element.getAttribute('data-bs-y')) || 0;
// Get the inner canvas (image)
const canvas = element.querySelector('.display-canvas');
if (canvas) {
const originalWidth = parseFloat(canvas.style.width);
const originalHeight = parseFloat(canvas.style.height);
const padding = this.padding; // Access the padding value
// Calculate rotated bounding box dimensions
const cosAngle = Math.abs(Math.cos(radians));
const sinAngle = Math.abs(Math.sin(radians));
const boundingWidth = originalWidth * cosAngle + originalHeight * sinAngle + 2 * padding;
const boundingHeight = originalWidth * sinAngle + originalHeight * cosAngle + 2 * padding;
// Update parent container to fit the rotated bounding box
element.style.width = `${boundingWidth}px`;
element.style.height = `${boundingHeight}px`;
// Center the canvas within the bounding box, accounting for padding
const offsetX = (boundingWidth - originalWidth) / 2 - padding;
const offsetY = (boundingHeight - originalHeight) / 2 - padding;
canvas.style.transform = `translate(${offsetX}px, ${offsetY}px) rotate(${radians}rad)`;
}
// Keep the bounding box positioned properly
element.style.transform = `translate(${x}px, ${y}px)`;
element.setAttribute('data-angle', radians);
},
handleRotationInputChange() {
const rotationInput = document.getElementById('rotation-input');
const degrees = parseFloat(rotationInput.value) || 0;
DraggableUtils.applyRotationToElement(DraggableUtils.lastInteracted, degrees);
},
storePageContents() {
var pagesMap = this.documentsMap.get(this.pdfDoc);
if (!pagesMap) {
@@ -325,7 +465,7 @@ const DraggableUtils = {
// render the page onto the canvas
var renderContext = {
canvasContext: this.pdfCanvas.getContext('2d'),
viewport: page.getViewport({scale: 1}),
viewport: page.getViewport({ scale: 1 }),
};
await page.render(renderContext).promise;
@@ -352,8 +492,6 @@ const DraggableUtils = {
this.loadPageContents();
}
},
parseTransform(element) {},
async getOverlayedPdfDocument() {
const pdfBytes = await this.pdfDoc.getData();
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
@@ -367,7 +505,6 @@ const DraggableUtils = {
if (pageIdx.includes('offset')) {
continue;
}
console.log(typeof pageIdx);
const page = pdfDocModified.getPage(parseInt(pageIdx));
let draggablesData = pagesMap[pageIdx];
@@ -376,45 +513,61 @@ const DraggableUtils = {
const offsetHeight = pagesMap[pageIdx + '-offsetHeight'];
for (const draggableData of draggablesData) {
// embed the draggable canvas
const draggableElement = draggableData.element;
// Embed the draggable canvas
const draggableElement = draggableData.element.querySelector('.display-canvas');
const response = await fetch(draggableElement.toDataURL());
const draggableImgBytes = await response.arrayBuffer();
const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes);
// calculate the position in the pdf document
const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, '');
const transformComponents = tansform.split(',');
// Extract transformation data
const transform = draggableData.element.style.transform || '';
const translateRegex = /translate\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px\)/;
const translateMatch = transform.match(translateRegex);
const translateX = translateMatch ? parseFloat(translateMatch[1]) : 0;
const translateY = translateMatch ? parseFloat(translateMatch[2]) : 0;
const childTransform = draggableElement.style.transform || '';
const childTranslateMatch = childTransform.match(translateRegex);
const childOffsetX = childTranslateMatch ? parseFloat(childTranslateMatch[1]) : 0;
const childOffsetY = childTranslateMatch ? parseFloat(childTranslateMatch[2]) : 0;
const rotateAngle = parseFloat(draggableData.element.getAttribute('data-angle')) || 0;
const draggablePositionPixels = {
x: parseFloat(transformComponents[0]),
y: parseFloat(transformComponents[1]),
width: draggableData.offsetWidth,
height: draggableData.offsetHeight,
x: translateX + childOffsetX + this.padding + 2,
y: translateY + childOffsetY + this.padding + 2,
width: parseFloat(draggableElement.style.width),
height: parseFloat(draggableElement.style.height),
angle: rotateAngle, // Store rotation
};
//Auxiliary variables
// Auxiliary variables
let widthAdjusted = page.getWidth();
let heightAdjusted = page.getHeight();
const rotation = page.getRotation();
//Normalizing angle
// Normalize page rotation angle
let normalizedAngle = rotation.angle % 360;
if (normalizedAngle < 0) {
normalizedAngle += 360;
}
//Changing the page dimension if the angle is 90 or 270
// Adjust page dimensions for rotated pages
if (normalizedAngle === 90 || normalizedAngle === 270) {
let widthTemp = widthAdjusted;
widthAdjusted = heightAdjusted;
heightAdjusted = widthTemp;
[widthAdjusted, heightAdjusted] = [heightAdjusted, widthAdjusted];
}
const draggablePositionRelative = {
x: draggablePositionPixels.x / offsetWidth,
y: draggablePositionPixels.y / offsetHeight,
width: draggablePositionPixels.width / offsetWidth,
height: draggablePositionPixels.height / offsetHeight,
angle: draggablePositionPixels.angle,
};
const draggablePositionPdf = {
x: draggablePositionRelative.x * widthAdjusted,
y: draggablePositionRelative.y * heightAdjusted,
@@ -422,11 +575,13 @@ const DraggableUtils = {
height: draggablePositionRelative.height * heightAdjusted,
};
//Defining the image if the page has a 0-degree angle
// Calculate position based on normalized page rotation
let x = draggablePositionPdf.x;
let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
//Defining the image position if it is at other angles
let originx = x + draggablePositionPdf.width / 2;
let originy = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height / 2;
if (normalizedAngle === 90) {
x = draggablePositionPdf.y + draggablePositionPdf.height;
y = draggablePositionPdf.x;
@@ -437,17 +592,32 @@ const DraggableUtils = {
x = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
y = widthAdjusted - draggablePositionPdf.x;
}
// draw the image
// let angle = draggablePositionPixels.angle % 360;
// if (angle < 0) angle += 360; // Normalize to positive angle
const radians = -draggablePositionPixels.angle; // Convert angle to radians
page.pushOperators(
PDFLib.pushGraphicsState(),
PDFLib.concatTransformationMatrix(1, 0, 0, 1, originx, originy),
PDFLib.concatTransformationMatrix(
Math.cos(radians),
Math.sin(radians),
-Math.sin(radians),
Math.cos(radians),
0,
0
),
PDFLib.concatTransformationMatrix(1, 0, 0, 1, -1 * originx, -1 * originy)
);
page.drawImage(pdfImageObject, {
x: x,
y: y,
width: draggablePositionPdf.width,
height: draggablePositionPdf.height,
rotate: rotation,
});
page.pushOperators(PDFLib.popGraphicsState());
}
}
this.loadPageContents();
return pdfDocModified;
},

View File

@@ -9,6 +9,7 @@ if (!isScriptExecuted) {
document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput);
});
}
let hasDroppedImage = false;
function setupFileInput(chooser) {
const elementId = chooser.getAttribute('data-bs-element-id');
@@ -18,6 +19,11 @@ function setupFileInput(chooser) {
let inputContainer = document.getElementById(inputContainerId);
if (inputContainer.id === 'pdf-upload-input-container') {
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.dragAndDropPDF;
} else if (inputContainer.id === 'image-upload-input-container') {
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.dragAndDropImage;
}
let allFiles = [];
let overlay;
let dragCounter = 0;
@@ -141,12 +147,17 @@ function setupFileInput(chooser) {
files.forEach((file) => dataTransfer.items.add(file));
return dataTransfer;
}
function handleFileInputChange(inputElement) {
const files = allFiles;
showOrHideSelectedFilesContainer(files);
const filesInfo = files.map((f) => ({name: f.name, size: f.size, uniqueId: f.uniqueId}));
const filesInfo = files.map((f) => ({
name: f.name,
size: f.size,
uniqueId: f.uniqueId,
type: f.type,
url: URL.createObjectURL(f),
}));
const selectedFilesContainer = $(inputContainer).siblings('.selected-files');
selectedFilesContainer.empty();
@@ -157,30 +168,111 @@ function setupFileInput(chooser) {
$(fileContainer).addClass(fileContainerClasses);
$(fileContainer).attr('id', info.uniqueId);
let fileIconContainer = createFileIconContainer(info);
let fileIconContainer = document.createElement('div');
const isDragAndDropEnabled =
window.location.pathname.includes('add-image') || window.location.pathname.includes('sign');
if (info.type.startsWith('image/')) {
let imgPreview = document.createElement('img');
imgPreview.src = info.url;
imgPreview.alt = 'Preview';
imgPreview.style.width = '50px';
imgPreview.style.height = '50px';
imgPreview.style.objectFit = 'cover';
$(fileIconContainer).append(imgPreview);
if (isDragAndDropEnabled) {
let dragIcon = document.createElement('div');
dragIcon.classList.add('drag-icon');
dragIcon.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M360-160q-33 0-56.5-23.5T280-240q0-33 23.5-56.5T360-320q33 0 56.5 23.5T440-240q0 33-23.5 56.5T360-160Zm240 0q-33 0-56.5-23.5T520-240q0-33 23.5-56.5T600-320q33 0 56.5 23.5T680-240q0 33-23.5 56.5T600-160ZM360-400q-33 0-56.5-23.5T280-480q0-33 23.5-56.5T360-560q33 0 56.5 23.5T440-480q0 33-23.5 56.5T360-400Zm240 0q-33 0-56.5-23.5T520-480q0-33 23.5-56.5T600-560q33 0 56.5 23.5T680-480q0 33-23.5 56.5T600-400ZM360-640q-33 0-56.5-23.5T280-720q0-33 23.5-56.5T360-800q33 0 56.5 23.5T440-720q0 33-23.5 56.5T360-640Zm240 0q-33 0-56.5-23.5T520-720q0-33 23.5-56.5T600-800q33 0 56.5 23.5T680-720q0 33-23.5 56.5T600-640Z"/></svg>';
fileContainer.appendChild(dragIcon);
$(fileContainer).attr('draggable', 'true');
$(fileContainer).on('dragstart', (e) => {
e.originalEvent.dataTransfer.setData('fileUrl', info.url);
e.originalEvent.dataTransfer.setData('uniqueId', info.uniqueId);
e.originalEvent.dataTransfer.setDragImage(imgPreview, imgPreview.width / 2, imgPreview.height / 2);
});
enableImagePreviewOnClick(fileIconContainer);
} else {
$(fileContainer).removeAttr('draggable');
}
} else {
fileIconContainer = createFileIconContainer(info);
}
let fileInfoContainer = createFileInfoContainer(info);
let removeBtn = document.createElement('div');
removeBtn.classList.add('remove-selected-file');
if (!isDragAndDropEnabled) {
let removeBtn = document.createElement('div');
removeBtn.classList.add('remove-selected-file');
let removeBtnIconHTML = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#C02223"><path d="m339-288 141-141 141 141 51-51-141-141 141-141-51-51-141 141-141-141-51 51 141 141-141 141 51 51ZM480-96q-79 0-149-30t-122.5-82.5Q156-261 126-331T96-480q0-80 30-149.5t82.5-122Q261-804 331-834t149-30q80 0 149.5 30t122 82.5Q804-699 834-629.5T864-480q0 79-30 149t-82.5 122.5Q699-156 629.5-126T480-96Z"/></svg>`;
$(removeBtn).append(removeBtnIconHTML);
$(removeBtn).attr('data-file-id', info.uniqueId).click(removeFileListener);
$(fileContainer).append(fileIconContainer);
$(fileContainer).append(fileInfoContainer);
$(fileContainer).append(removeBtn);
let removeBtnIconHTML = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#C02223"><path d="m339-288 141-141 141 141 51-51-141-141 141-141-51-51-141 141-141-141-51 51 141 141-141 141 51 51ZM480-96q-79 0-149-30t-122.5-82.5Q156-261 126-331T96-480q0-80 30-149.5t82.5-122Q261-804 331-834t149-30q80 0 149.5 30t122 82.5Q804-699 834-629.5T864-480q0 79-30 149t-82.5 122.5Q699-156 629.5-126T480-96Z"/></svg>`;
$(removeBtn).append(removeBtnIconHTML);
$(removeBtn).attr('data-file-id', info.uniqueId).click(removeFileListener);
$(fileContainer).append(removeBtn);
}
$(fileContainer).append(fileIconContainer, fileInfoContainer);
selectedFilesContainer.append(fileContainer);
});
const pageContainers = $('#box-drag-container');
pageContainers.off('dragover').on('dragover', (e) => {
e.preventDefault();
});
showOrHideSelectedFilesContainer(filesInfo);
pageContainers.off('drop').on('drop', (e) => {
e.preventDefault();
const fileUrl = e.originalEvent.dataTransfer.getData('fileUrl');
if (fileUrl) {
const existingImages = $(e.target).find(`img[src="${fileUrl}"]`);
if (existingImages.length === 0) {
DraggableUtils.createDraggableCanvasFromUrl(fileUrl);
}
}
const overlayElement = chooser.querySelector('.drag-drop-overlay');
if (overlayElement) {
overlayElement.style.display = 'none';
}
hasDroppedImage = true;
});
showOrHideSelectedFilesContainer(files);
}
function showOrHideSelectedFilesContainer(files) {
if (files && files.length > 0) chooser.style.setProperty('--selected-files-display', 'flex');
else chooser.style.setProperty('--selected-files-display', 'none');
if (files && files.length > 0) {
chooser.style.setProperty('--selected-files-display', 'flex');
} else {
chooser.style.setProperty('--selected-files-display', 'none');
}
const isDragAndDropEnabled =
(window.location.pathname.includes('add-image') || window.location.pathname.includes('sign')) &&
files.some((file) => file.type.startsWith('image/'));
if (!isDragAndDropEnabled) return;
const selectedFilesContainer = chooser.querySelector('.selected-files');
let overlayElement = chooser.querySelector('.drag-drop-overlay');
if (!overlayElement) {
selectedFilesContainer.style.position = 'relative';
overlayElement = document.createElement('div');
overlayElement.classList.add('draggable-image-overlay');
overlayElement.innerHTML = 'Drag images to add them to the page';
selectedFilesContainer.appendChild(overlayElement);
}
if (hasDroppedImage) overlayElement.style.display = files && files.length > 0 ? 'flex' : 'none';
selectedFilesContainer.addEventListener('mouseenter', () => {
overlayElement.style.display = 'none';
});
selectedFilesContainer.addEventListener('mouseleave', () => {
if (!hasDroppedImage) overlayElement.style.display = files && files.length > 0 ? 'flex' : 'none';
});
}
function removeFileListener(e) {
@@ -235,4 +327,52 @@ function setupFileInput(chooser) {
removeFileById(fileId, inputElement);
showOrHideSelectedFilesContainer(allFiles);
});
function enableImagePreviewOnClick(container) {
const imagePreviewModal = document.getElementById('imagePreviewModal') || createImagePreviewModal();
container.querySelectorAll('img').forEach((img) => {
if (!img.hasPreviewListener) {
img.addEventListener('mouseup', function () {
const imgElement = imagePreviewModal.querySelector('img');
imgElement.src = this.src;
imagePreviewModal.style.display = 'flex';
});
img.hasPreviewListener = true;
}
});
function createImagePreviewModal() {
const modal = document.createElement('div');
modal.id = 'imagePreviewModal';
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100vw';
modal.style.height = '100vh';
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
modal.style.display = 'none';
modal.style.justifyContent = 'center';
modal.style.alignItems = 'center';
modal.style.zIndex = '2000';
const imgElement = document.createElement('img');
imgElement.style.maxWidth = '90%';
imgElement.style.maxHeight = '90%';
modal.appendChild(imgElement);
document.body.appendChild(modal);
modal.addEventListener('click', () => {
modal.style.display = 'none';
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.style.display === 'flex') {
modal.style.display = 'none';
}
});
return modal;
}
}
}

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