Compare commits

...

281 Commits

Author SHA1 Message Date
Anthony Stirling
0f8d2937eb Merge pull request #1275 from Stirling-Tools/sync_version
💾 Update Version
2024-05-22 22:33:25 +01:00
Anthony Stirling
53fcc51541 Merge pull request #1274 from Stirling-Tools/test
cleanup html for reverse proxy
2024-05-22 22:33:10 +01:00
GitHub Action action@github.com
23b60c73a0 💾 Sync Versions
> Made via sync_files.yml
2024-05-22 21:32:26 +00:00
Anthony Stirling
65321991c6 Update build.gradle 2024-05-22 22:32:11 +01:00
Anthony Stirling
9d56014ca0 thymeleaf 2024-05-22 21:48:23 +01:00
Anthony Stirling
7b2493a838 test random stuff 2024-05-22 21:45:35 +01:00
Anthony Stirling
1d4db6493d Merge pull request #1265 from pcanham/feature/expand-sso-dockercompose-example
Add in oauth scope environment variable to docker-compose example
2024-05-22 19:41:00 +01:00
Anthony Stirling
c4bfb44f72 Merge pull request #1272 from t71rs/Update-ReadMe
Updated ReadMe Screenshot
2024-05-22 19:40:34 +01:00
Anthony Stirling
d9e7ae1380 Update README.md 2024-05-22 19:11:18 +01:00
ge64qev
47b10d45d2 Fixed black edge at the side of the screenshot 2024-05-22 11:44:29 +02:00
ge64qev
ffd27acedc Changed screenshot in ReadMe with a screenshot of new UI 2024-05-22 09:30:07 +02:00
Paul Canham
841b8a6439 chore: correcting typo in docker-compose example for sso 2024-05-21 23:01:40 +01:00
Paul Canham
611d2b22d2 feat: expand on sso example showing ability to alter oauth2 scope 2024-05-21 23:00:49 +01:00
Anthony Stirling
4bad105119 Merge pull request #1204 from t71rs/Deletion-of-Files-using-Merge
User Friendly Merge File Selection
2024-05-21 20:44:47 +01:00
Anthony Stirling
c7b3f89f48 Merge pull request #1256 from zallaevan/main
Amended typo: trailing space
2024-05-21 20:43:16 +01:00
Anthony Stirling
1dd0852e54 Merge pull request #1263 from Houba28/cz_translations
Czech translations
2024-05-21 20:42:58 +01:00
Jan Vochomurka
c1bb1002f5 Merge branch 'main' into cz_translations 2024-05-21 14:37:54 +02:00
Jan Vochomurka
367146b9ad added czech translations 2024-05-21 14:34:49 +02:00
Evan Zhang
1f1cdf6fe8 Amended typo: trailing space
Trailing space before colon in the `settings.yml` has been fixed.
2024-05-21 00:50:03 +02:00
Anthony Stirling
84b355951f Merge pull request #1255 from limonkufu/main
Add missing Turkish translation
2024-05-20 23:32:59 +01:00
Ugur Yilmaz
bfb82e38ab Add missing translation and improve some wordings to be more natural for Turkish 2024-05-20 21:48:11 +00:00
Anthony Stirling
149249c6ac Merge pull request #1253 from Stirling-Tools/configFixes
Config fixes
2024-05-20 18:46:59 +01:00
Anthony Stirling
4232f359c7 bump 2024-05-20 18:44:13 +01:00
Anthony Stirling
6adeecac9c fix tool removal in navbar 2024-05-20 18:43:47 +01:00
a
0cc12f4456 Merge branch 'main' of git@github.com:Stirling-Tools/Stirling-PDF.git into main 2024-05-20 18:18:40 +01:00
Anthony Stirling
2646af19b3 config fix and book icons 2024-05-20 18:18:26 +01:00
Anthony Stirling
ea02369c76 Merge pull request #1243 from Stirling-Tools/sync_readme
📝 Update README: Translation Progress Table
2024-05-19 23:26:49 +01:00
Anthony Stirling
e48c125f2c Merge pull request #1244 from Stirling-Tools/sync_version
💾 Update Version
2024-05-19 23:26:40 +01:00
GitHub Action action@github.com
46c9055e11 💾 Sync Versions
> Made via sync_files.yml
2024-05-19 22:26:29 +00:00
GitHub Action action@github.com
d9206df038 📝 Sync README
> Made via sync_files.yml
2024-05-19 22:26:28 +00:00
Anthony Stirling
5dee084ad6 Update build.gradle 2024-05-19 23:26:15 +01:00
Anthony Stirling
2319ab3d9e Update home.html 2024-05-19 23:17:04 +01:00
Anthony Stirling
7fbb94f2bd Merge pull request #1240 from leknoppix/main
🐛 FIX: Update French Traduction
2024-05-19 22:25:48 +01:00
Pascal Canadas
4167e13f76 🐛 FIX: Update French Traduction 2024-05-19 22:03:37 +02:00
Anthony Stirling
1609173907 Merge pull request #1237 from Stirling-Tools/sync_readme
📝 Update README: Translation Progress Table
2024-05-19 17:59:53 +01:00
Anthony Stirling
d142f0abd6 Merge pull request #1236 from Stirling-Tools/sync_version
💾 Update Version
2024-05-19 16:56:25 +01:00
Anthony Stirling
c2949d8944 Merge pull request #1234 from Stirling-Tools/sync_readme
📝 Update README: Translation Progress Table
2024-05-19 16:56:18 +01:00
GitHub Action action@github.com
4b2d02ee14 📝 Sync README
> Made via sync_files.yml
2024-05-19 15:56:17 +00:00
GitHub Action action@github.com
4766201621 💾 Sync Versions
> Made via sync_files.yml
2024-05-19 15:56:15 +00:00
Anthony Stirling
d9043c9100 Update build.gradle 2024-05-19 16:56:02 +01:00
Anthony Stirling
d52b0d0082 Update merge-pdfs.html 2024-05-19 16:55:44 +01:00
GitHub Action action@github.com
ecfbaef933 📝 Sync README
> Made via sync_files.yml
2024-05-19 15:17:23 +00:00
Anthony Stirling
c5e6555bb5 Merge pull request #1233 from HNPorts/main
Update messages_tr_TR.properties
2024-05-19 16:17:09 +01:00
Han
81d7cc0a40 Update messages_tr_TR.properties 2024-05-19 18:02:27 +03:00
Anthony Stirling
02524c64d5 Merge pull request #1230 from Stirling-Tools/sync_version
💾 Update Version
2024-05-19 13:28:31 +01:00
GitHub Action action@github.com
7d6846920e 💾 Sync Versions
> Made via sync_files.yml
2024-05-19 12:27:16 +00:00
Anthony Stirling
2443ed2020 quick cache fix 2024-05-19 13:27:00 +01:00
Anthony Stirling
0dd91f209a Merge pull request #1229 from Stirling-Tools/sync_readme
📝 Update README: Translation Progress Table
2024-05-19 12:26:55 +01:00
GitHub Action action@github.com
869e2dc62d 📝 Sync README
> Made via sync_files.yml
2024-05-19 11:06:36 +00:00
Anthony Stirling
a28f14b70e Merge pull request #1227 from albanobattistella/patch-26
Update messages_it_IT.properties
2024-05-19 12:06:23 +01:00
Anthony Stirling
b5de6a73cc Merge pull request #1228 from Stirling-Tools/config
Config
2024-05-19 12:05:47 +01:00
Anthony Stirling
45e2623b9b change configs 2024-05-19 12:00:46 +01:00
albanobattistella
6d95bfdee0 Update messages_it_IT.properties 2024-05-19 12:57:34 +02:00
a
f9111e556c Merge remote-tracking branch 'origin/main' into config 2024-05-19 11:54:58 +01:00
Anthony Stirling
fa746a2b51 config stuff 2024-05-19 11:54:45 +01:00
Anthony Stirling
e43292f4dd Merge pull request #1226 from Ludy87/validation_username_password
bg-card will be added to the class and password/username validation
2024-05-19 11:52:52 +01:00
Anthony Stirling
c56c5f80ab Merge pull request #1225 from Ludy87/add_missing_settings
adds all available settings to settings.yml
2024-05-19 11:52:04 +01:00
Ludy87
f2eb5dd7d3 bg-card will be added to the class and password/username validation
bg-card should not be an id, ids should be unique in their use.
2024-05-19 12:44:54 +02:00
Ludy87
ffe221b93c Update README.md 2024-05-19 11:36:50 +02:00
Ludy87
3f252e29a1 adds all available settings to settings.yml 2024-05-19 11:35:46 +02:00
Anthony Stirling
7c0fd02126 Merge pull request #1224 from Ludy87/fix_system_exit
Fix: Removes username validation check
2024-05-19 10:25:41 +01:00
Ludy87
7109dd7905 Fix: Removes username validation check
- Removes username validation check
- Ignores API users in user counting
2024-05-19 10:52:11 +02:00
Anthony Stirling
e30665e7c8 Merge pull request #1223 from Stirling-Tools/sync_version
💾 Update Version
2024-05-19 00:03:17 +01:00
GitHub Action action@github.com
514606789e 💾 Sync Versions
> Made via sync_files.yml
2024-05-18 23:00:29 +00:00
Anthony Stirling
dd1a441f92 Merge pull request #1222 from Stirling-Tools/minorSizes
sizes
2024-05-19 00:00:15 +01:00
Anthony Stirling
31c48aec90 sizes 2024-05-18 23:59:40 +01:00
Anthony Stirling
6709e0c46d Merge pull request #1221 from Stirling-Tools/sync_readme
📝 Update README: Translation Progress Table
2024-05-18 23:33:54 +01:00
GitHub Action action@github.com
10dd5e4a40 📝 Sync README
> Made via sync_files.yml
2024-05-18 22:30:52 +00:00
Anthony Stirling
6fc9a2032a Merge pull request #1201 from Ludy87/add_functions_oauth2
extends the functionality of oauth in Stirling PDF
2024-05-18 23:30:37 +01:00
Ludy87
ffec5f7b54 extends the functionality of oauth in Stirling PDF 2. 2024-05-18 23:47:05 +02:00
Anthony Stirling
b904a46bca Update CustomAuthenticationSuccessHandler.java 2024-05-18 19:39:35 +01:00
Anthony Stirling
26a457f9d0 Update InitialSecuritySetup.java 2024-05-18 19:38:39 +01:00
Anthony Stirling
e9042e0b7e Update login.html 2024-05-18 19:37:11 +01:00
Anthony Stirling
521dff737f Merge branch 'main' into add_functions_oauth2 2024-05-18 19:24:02 +01:00
Anthony Stirling
2968a696cd error banner fixes 2024-05-18 18:40:13 +01:00
a
e0d3bbf13b Merge branch 'main' of git@github.com:Stirling-Tools/Stirling-PDF.git into main 2024-05-18 18:39:46 +01:00
Anthony Stirling
89e763d959 error banner fixes 2024-05-18 18:39:38 +01:00
Anthony Stirling
1f9a0ed0e3 Merge pull request #1193 from Stirling-Tools/redesign
Redesign
2024-05-18 14:14:31 +01:00
Anthony Stirling
f203e07f55 Merge remote-tracking branch 'origin/main' into redesign 2024-05-18 13:53:34 +01:00
Anthony Stirling
56d4c02445 readd searchbar 2024-05-18 13:43:00 +01:00
Ludy
c20936b485 Merge branch 'main' into add_functions_oauth2 2024-05-18 13:52:53 +02:00
Anthony Stirling
389323c190 transition only on change not load 2024-05-18 12:48:01 +01:00
Anthony Stirling
f0dd48b3b1 font load detection 2024-05-18 12:47:21 +01:00
Anthony Stirling
b860146c93 logging for #1024 and jdk bump 2024-05-17 19:18:57 +01:00
Anthony Stirling
23b662e5dc Merge pull request #1211 from Stirling-Tools/sync_readme
📝 Update README: Translation Progress Table
2024-05-15 22:27:38 +01:00
GitHub Action action@github.com
7160ba47b1 📝 Sync README
> Made via sync_files.yml
2024-05-15 08:34:47 +00:00
Anthony Stirling
bdea990b18 Merge pull request #1208 from p3lo/slovakia-translation
Slovak translation
2024-05-15 09:34:32 +01:00
p3lo
1cbc6307c3 Added Slovak language to supported languages table 2024-05-15 10:13:03 +02:00
p3lo
1e42f54ec7 Added slovak translation 2024-05-14 22:56:36 +02:00
Anthony Stirling
44e85a1d38 Merge pull request #1205 from foivospro/main
Fix rotation function
2024-05-14 18:01:15 +01:00
Anthony Stirling
54073767af Merge pull request #1207 from urjeetpatel/main
fix Remove Redundant Back to Main Page Button
2024-05-14 18:01:02 +01:00
Urjeet Patel
3fa41e058a fix: Remove Redundant Back to Main Page Button
Remove Redundant Back to Main Page Button. this fixes #489

Closes #489
2024-05-14 16:43:18 +00:00
foiv
44f9313012 Fix Rotation function 2024-05-14 12:11:30 +03:00
ge64qev
ce3e98e240 Deleted Console logs and adjusted some comments. 2024-05-14 09:13:39 +02:00
ge64qev
36192ba560 Change the SortFiles Method, so it works with the filesWithUniqueID Array 2024-05-14 08:49:27 +02:00
ge64qev
0e262dc2bd Added a unique id for all files to be a able to delete duplicate files. 2024-05-13 22:27:25 +02:00
Ludy87
dcf13e9ade Update InitialSecuritySetup.java 2024-05-12 20:17:46 +02:00
Ludy87
811c19e00d extends the functionality of oauth in Stirling PDF 2024-05-12 19:58:34 +02:00
Anthony Stirling
f2b7aeeb1c Merge pull request #1199 from jimdouk/fixFrontEndIssue
Enhance UI Search Functionality in Navbar
2024-05-12 14:36:52 +01:00
Dimitris Doukas
840694c527 Update search functionality on navbar 2024-05-12 14:07:43 +03:00
Dimitris Doukas
21e5002d73 Merge branch 'Stirling-Tools:main' into fixFrontEndIssue 2024-05-12 13:55:51 +03:00
Rectos VX
36d6c06237 Update: fix dropdown hover + icon titles on mobile 2024-05-10 15:44:54 +04:00
ge64qev
c1fea7c92f A duplicate Warning is displayed if the same file is added twice to the merging process 2024-05-10 10:53:27 +02:00
Anthony Stirling
29ec42bc35 Merge branch 'main' into redesign 2024-05-09 19:49:06 +01:00
Anthony Stirling
425502b3e3 Merge pull request #1192 from Stirling-Tools/sync_readme
📝 Update README: Translation Progress Table
2024-05-09 17:50:44 +01:00
Anthony Stirling
692a526900 Merge pull request #1188 from bluestero/main
Install locally on a machine with no-root, docker, apt, yum, and apk via nix package manager.
2024-05-09 17:50:30 +01:00
Rectos VX
3a27d97811 Update: change update icon + animation 2024-05-09 17:31:12 +04:00
GitHub Action action@github.com
af91c73e7a 📝 Sync README
> Made via sync_files.yml
2024-05-08 21:22:18 +00:00
Anthony Stirling
526a30d033 Merge pull request #1191 from nimdassdev/main
Updated/improved Bulgarian language strings. Thank you.
2024-05-08 22:22:03 +01:00
IT Creativity + Art Team
bf8d6d2337 Update messages_bg_BG.properties
Updated/improved Bulgarian language strings. Thank you.
2024-05-08 23:43:51 +03:00
IT Creativity + Art Team
5628300f51 Merge branch 'Stirling-Tools:main' into main 2024-05-08 23:00:20 +03:00
Rectos VX
72ba97a00c Update: open dropdown on hover 2024-05-08 20:21:07 +04:00
Rectos VX
1634987171 Update: local font + fix home card layout 2024-05-08 18:24:55 +04:00
Rectos VX
0f43723250 Update: fix dropdown text wrap 2024-05-08 17:42:36 +04:00
Rectos VX
34e2128a39 Update: fix error page theme 2024-05-08 16:26:14 +04:00
Ahmed Khatib
0a0887aafc Fixed the bad working regarding language packs. Explained the dbus variable change before running the jar file. 2024-05-08 01:52:41 +05:30
Anthony Stirling
7c0c33ca63 Merge pull request #1180 from subarudad/fix_readme
Update README.md: minor spelling fix
2024-05-07 21:04:59 +01:00
Ahmed Khatib
8b2f24affd Added the export command before running. 2024-05-08 01:22:52 +05:30
Ahmed Khatib
cbe750c76c Small typo in apt-get update. 2024-05-08 01:15:43 +05:30
Ahmed Khatib
5f6d24f805 Added support for non-root and nix package manager for local installation. Fixed a bad small line break issue. 2024-05-08 01:14:08 +05:30
Anthony Stirling
be5d5fdf04 Merge pull request #1179 from Stirling-Tools/pixeebot/drip-2024-05-07-pixee-java/strip-http-header-newlines
Introduced protections against HTTP header injection / smuggling attacks
2024-05-07 19:14:29 +01:00
brucengumetro
a04dc605df minor spelling fix 2024-05-07 16:05:01 +07:00
pixeebot[bot]
503acc9408 Introduced protections against HTTP header injection / smuggling attacks 2024-05-07 03:44:03 +00:00
Anthony Stirling
9b166da57d Merge pull request #1177 from albanobattistella/patch-25
Update messages_it_IT.properties
2024-05-06 12:45:29 +01:00
albanobattistella
66e566555e Update messages_it_IT.properties 2024-05-06 13:32:23 +02:00
ge64qev
7ba0067688 Made it possible to add files via the selector "File by File". 2024-05-06 11:18:59 +02:00
Anthony Stirling
f7aebf22c8 Merge pull request #1172 from Stirling-Tools/sync_version
💾 Update Version
2024-05-05 21:08:23 +01:00
GitHub Action action@github.com
bb0b5f0528 💾 Sync Versions
> Made via sync_files.yml
2024-05-05 19:50:36 +00:00
Anthony Stirling
9e3d5a5bc5 Update build.gradle 2024-05-05 20:50:22 +01:00
Anthony Stirling
9ba5c6d4be Merge pull request #1171 from Stirling-Tools/timeouts
Timeouts #1094
2024-05-05 20:47:17 +01:00
Anthony Stirling
00f7fe7ac3 Update application.properties 2024-05-05 20:46:22 +01:00
Anthony Stirling
f4fcede771 Update ProcessExecutor.java 2024-05-05 20:45:52 +01:00
Anthony Stirling
b69646d00b Merge branch 'main' into 0.22.8Clone 2024-05-05 20:28:25 +01:00
Anthony Stirling
9e81b161c3 Merge pull request #1170 from Rectos28/redesign
Redesign using MD3 system
2024-05-05 18:46:44 +01:00
github-actions[bot]
66ce7511ca 📝 Update README: Translation Progress Table (#1168)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-05-05 14:01:42 +01:00
Anthony Stirling
d17db24aa9 #1160 update rhefs (#1169)
Update common.html
2024-05-05 13:53:26 +01:00
Anthony Stirling
ac5273244c flatten (#1167) 2024-05-05 13:33:17 +01:00
Rectos VX
27113f99cb Remove: old icon svg file 2024-05-05 15:46:06 +04:00
Rectos VX
fa31a4e340 Update: some margins in some places 2024-05-05 15:43:03 +04:00
Rectos VX
4d53119390 Update: add new tags to all languages 2024-05-05 15:23:42 +04:00
Rectos VX
303b8e032b Update: updated all pages to new theme system 2024-05-05 15:19:53 +04:00
Anthony Stirling
d6b1fec69d Update Dockerfile 2024-05-05 12:18:52 +01:00
Rectos VX
5c572a7d89 Update: changed JS to new theme system + Darkmode system 2024-05-05 15:12:30 +04:00
Rectos VX
04d1ff3822 Update: Changed pages css to adapte new theme system 2024-05-05 15:07:44 +04:00
Rectos VX
eb8a494b5c Remove: Deleted old theme css 2024-05-05 14:53:22 +04:00
Rectos VX
4dfac2f46f Add: New theme css system 2024-05-05 14:50:36 +04:00
albanobattistella
547f231e29 Update messages_it_IT.properties (#1165) 2024-05-05 11:40:18 +01:00
Anthony Stirling
38979dd362 lets try this again (Config fix) (#1159)
* Introducing a custom settings file

* formats

* chnages

* Update README.md

* fixes

---------

Co-authored-by: a <a>
2024-05-03 22:23:21 +01:00
Anthony Stirling
890163053b introduces custom settings file (#1158)
* Introducing a custom settings file

* formats

* chnages

* Update README.md
2024-05-03 20:43:48 +01:00
t71rs
fbbc71d7e6 Drag&drop bug (#1157)
* Fixed issue that Drag and Droppped Files can not be deleted

* Deleted unnecessary Comment
2024-05-03 19:09:36 +01:00
Anthony Stirling
4372536c17 Remove ChatGPT mention to avoid confusion 2024-05-03 10:40:22 +01:00
github-actions[bot]
7f577a6052 📝 Update README: Translation Progress Table (#1155)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-05-02 22:02:33 +01:00
Sahil Phule
d7afc574a6 Change User Roles (#1153)
* Modify user service and controller

* Modify Template

* Add messages

* Fix Username output

* Add tooltip

* Change Role Modify logic

* Add Roles from database to existing users

* Add default select Fillers

* Indent JS

* Add Change Role Related Translations

* Remove unnecessary Whitespace and imports
2024-05-02 21:52:50 +01:00
github-actions[bot]
c622ee915b 📝 Update README: Translation Progress Table (#1151)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-30 23:04:59 +01:00
albanobattistella
d0df392eef Update messages_it_IT.properties (#1148) 2024-04-30 23:04:05 +01:00
Ludy
1c33500815 Update messages_de_DE.properties (#1149) 2024-04-30 23:03:50 +01:00
t71rs
d730c6a12f Arrow key support signing (#1143)
* Added Arrow Key functionality for the Sign page.

* Added Arrow Key functionality for the Sign page.

* Adjusted step size, so it is relative to the size of the draggable

* Enabled Arrow Key support also for Add-Image

---------

Co-authored-by: Eric <71648843+sbplat@users.noreply.github.com>
2024-04-30 13:02:12 -04:00
Sahil Phule
b71f6f93b1 Add translations for OAUTH2 Related Text (#1146) 2024-04-30 08:02:15 +01:00
github-actions[bot]
32dd328048 Update 3rd Party Licenses (#1144)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-04-29 22:05:54 +01:00
Sahil Phule
d9fa8f7b48 Add OAUTH2 OIDC login support (#1140)
* Somewhat working

* Change Autocreate logic

* Add OAuth Error Message if Auto create Disabled

* Display OAUTH2 username(email) in Account Settings

* Disable Change user/pass for Oauth2 user

* Hide SSO Button if SSO login Disabled

* Remove some spaces and comments

* Add OAUTH2 Login example docker-compose file

* Add Some Comments

* Hide Printing of Client secret

* Remove OAUTH2 Beans

and replace with applicationProperties

* Add conditional annotation to Bean Creation

* Update settings.yml.template

Add OAUTH2 enabling template.

* Update messages_en_GB.properties
2024-04-29 22:01:22 +01:00
Anthony Stirling
777e512e61 fonts noto fix #974 (#1139)
fix #974
2024-04-28 23:33:55 +01:00
Anthony Stirling
e7e3b34b37 fix for #1035 (#1137)
* fix for #1035

* Update ConvertImgPDFController.java
2024-04-28 22:37:40 +01:00
github-actions[bot]
6ed9e1c707 📝 Update README: Translation Progress Table (#1134)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-28 10:40:01 +01:00
Ludy
318076254d Handling Untranslatable Strings (#1133) 2024-04-27 23:26:12 +01:00
github-actions[bot]
4fea8d10f8 💾 Update Version (#1130)
💾 Sync Versions
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-27 11:04:28 +01:00
Anthony Stirling
8c9d6f7b66 Custom HTML support #355 (#1129)
* test

* settings

* version
2024-04-27 11:03:57 +01:00
Anthony Stirling
30444fc9bb commit (#1128)
* commit

* formatting
2024-04-26 23:27:40 +01:00
Han
70349d642b Update messages_tr_TR.properties (#1126)
* Update messages_tr_TR.properties

* Update messages_tr_TR.properties
2024-04-26 19:37:09 +01:00
albanobattistella
afaec64afd Update messages_it_IT.properties (#1120) 2024-04-24 21:57:56 +01:00
github-actions[bot]
2a9fdff605 📝 Update README: Translation Progress Table (#1119)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-24 19:59:53 +01:00
Anthony Stirling
34c7ee46a0 lang adds 2024-04-24 19:59:01 +01:00
phfuh
5ee702f364 PDF to PDF/A language files (#1118)
* Update messages_en_GB.properties

* Update messages_en_US.properties

* Update messages_de_DE.properties
2024-04-24 19:56:35 +01:00
Ludy
1b5d21a22e Bugfix: Login page shows wrong selected country flag/language (#1117)
Closes #1116
2024-04-24 09:10:16 +01:00
Anthony Stirling
c3e5157dee Delete CNAME 2024-04-23 22:35:21 +01:00
Anthony Stirling
6d859e4c25 Create CNAME 2024-04-23 22:34:29 +01:00
diemade
95a9aca5b5 Update CONTRIBUTING.md (#1115)
adding a chapter on how to edit Docs
2024-04-23 22:32:12 +01:00
github-actions[bot]
4c8f582c56 💾 Update Version (#1108)
💾 Sync Versions
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-21 23:08:06 +01:00
Anthony Stirling
71e93e3cb5 print (WIP), fake scan (WIP) and text conversion for ultra-lite (#1098)
* Changes!

* lang

* fake scan init, print init and pdf to text for exe

* Hardening suggestions for Stirling-PDF / changes (#1099)

* Switch order of literals to prevent NullPointerException

* Introduced protections against predictable RNG abuse

---------

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>

* Update README.md

* install custom fonts

* Formats etc

* version bump

* disable WIP work

* remove chinese font

---------

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
Co-authored-by: systo <systo@host.docker.internal>
2024-04-21 23:06:44 +01:00
github-actions[bot]
6c052a7b25 📝 Update README: Translation Progress Table (#1107)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-21 23:00:40 +01:00
NeilJared
1e0ec8345a >Updated es_ES (100% completed) (#1106)
Update messages_es_ES.properties

Updated es_ES translation (100% done)
2024-04-21 22:50:36 +01:00
diemade
eddcc11fe4 German translation for Print Tool (#1104)
* Update messages_de_DE.properties

Only full sentences should end in a period,

* Update messages_de_DE.properties

* Update messages_de_DE.properties

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-21 22:31:45 +01:00
Ludy
3189d9dda8 Check DOCKER_ENABLE_SECURITY for UI (#1103)
When using `DOCKER_ENABLE_SECURITY=false`, the logout button and `Account Settings` are no longer displayed.
2024-04-21 21:16:39 +01:00
Ludy
5185fd13b8 updating the build files (#1100) 2024-04-21 16:30:17 +01:00
Ludy
a5000fbbc5 UI: settings show/hide update display (#1072)
* UI: settings show/hide update display

This PR replaces the PR #1003

In this PR, the visual for available update is added to the foreground.

There are new settings to generally show/hide the update display, and only administrators receive the update display.

* change to `Bean`

* Update AppUpdateShowService.java

* add update message

* revision service

* change shouldShow

* Update githubVersion.js

* rm folder

* Update AppUpdateService.java
2024-04-21 12:15:18 +01:00
albanobattistella
e74a8e434b Update messages_it_IT.properties (#1096) 2024-04-21 09:16:05 +01:00
phfuh
b702f5772d Add selection for PDF/A output format (#1095)
* Create PdfToPdfARequest.java

* Change class, add output format

* Add input field for output format

* Change output format selection order
2024-04-21 08:44:05 +01:00
github-actions[bot]
214e23fd93 📝 Update README: Translation Progress Table (#1093)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-20 20:45:45 +01:00
Han
943071ebb7 Update messages_tr_TR.properties (#1092) 2024-04-20 19:18:20 +01:00
github-actions[bot]
c575ed2036 📝 Update README: Translation Progress Table (#1091)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-20 15:28:10 +01:00
Anthony Stirling
06a178cc03 Cache form inputs between runs (#1066)
* Changes!

* lang
2024-04-20 14:46:49 +01:00
diemade
73f90885b4 Update messages_de_DE.properties (#1069)
Only full sentences should end in a period,

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-20 10:45:58 +01:00
Dimitris Doukas
9402109663 Merge branch 'Stirling-Tools:main' into main 2024-04-15 17:05:08 +03:00
Ludy
ace4e200b1 Fix: Resolve Username Case Sensitivity Issue in Login Flow (#1070)
* Fix: Username changing

The only situation where the username must be unique is when changing the username.

* Update UserController.java
2024-04-14 22:07:03 +01:00
github-actions[bot]
032388a8e3 📝 Update README: Translation Progress Table (#1063)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-11 22:49:12 +01:00
albanobattistella
276b6e521a Update messages_it_IT.properties (#1062) 2024-04-11 22:40:05 +01:00
Ludy
35a4462a86 replace comma to dot (#1057)
In several countries the comma is used as a decimal, the PR will replace the comma with a dot.
2024-04-09 17:47:53 +01:00
pixeebot[bot]
5564f378e5 (Sonar) Fix "String#replace should be preferred to String#replaceAll" (#1056)
Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2024-04-09 06:52:52 +01:00
Ludy
66d5f3e4b5 add sync-readme (#1051) 2024-04-08 23:33:58 +01:00
Dimitris Kaitantzidis
0f367c23aa 359 split metadata lost (#1049)
* Closes #359

* Adds a minor fix, the modified date should be changed after a modification is made.
2024-04-08 20:53:00 +00:00
Ludy
7dd1679588 Ukrainian - add missing strings (#1048)
add missing strings
2024-04-08 21:37:30 +01:00
Foivos Proestakis
6b186d5d8e Improve Greek translation (#1046)
* Update and Improve Greek translation

# Update Greek Translations

## Description

- Updated translations for all pages and content on the website.
- Corrected grammatical errors or typos in the Greek translation.
- Improved the overall readability and flow of the translated text.

This commit aims to enhance the user experience for Greek-speaking users of the website.

* Update messages_el_GR.properties
2024-04-08 21:29:31 +01:00
Ludy
d53be3aa14 add Ukrainian README, languages.html and correction FR, IT (#1044) 2024-04-08 21:29:13 +01:00
Eric
3dbfde534e fix: missing pdf to html endpoint (#1043)
* fix: missing pdf to html endpoint

* refactor: remove unused variable

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-08 21:28:57 +01:00
vkykalo
9a57842ece Feature: Add Ukrainian Language Support (#1039)
* Add uk_UA lang

* Add added error translation block into  uk_UA lang

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-08 21:27:55 +01:00
CocoMaster-AI
ec83b9a17d Update ja_JP,ko_KR,ru_RU,zh_TW.properties (#1036) 2024-04-08 21:27:36 +01:00
Ludy
59a19b0091 Update messages_de_DE.properties (#1045) 2024-04-08 20:25:14 +00:00
Dimitris Kaitantzidis
471865e4a3 Closes #359 (#1047) 2024-04-08 21:23:03 +01:00
Anthony Stirling
3868b4eca2 updates 2024-04-05 13:05:16 +01:00
Han
64f8765115 Update messages_tr_TR.properties (#1032)
update TR
2024-04-05 12:58:54 +01:00
github-actions[bot]
3804656218 💾 Update Version (#1033)
💾 Sync Versions
> Made via sync_versions.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-05 12:58:05 +01:00
Anthony Stirling
a5d824213c OCR fix for #1019 2024-04-05 12:51:22 +01:00
albanobattistella
160a4e9f8d Update messages_it_IT.properties (#1031) 2024-04-04 21:52:24 +01:00
Ludy
74a0574462 Update progress of language (#1029) 2024-04-04 10:16:10 +01:00
Muhammad Sani Ibrahim
1cf23b3542 Update README.md (#606)
Make the English more standard

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-03 22:51:59 +01:00
github-actions[bot]
2ef1242cd8 💾 Update Version (#1015)
💾 Sync Versions
> Made via sync_versions.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-03 22:41:33 +01:00
pixeebot[bot]
54c3bee205 Replaced Stream.collect(Collectors.toList()) with Stream.toList() (Sonar) (#1018)
Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2024-04-03 22:41:24 +01:00
NicolasFR
a63c0a3625 lang: update fr_FR (#1020)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-03 22:41:07 +01:00
Dimitris Doukas
3103a0bffc Enhance description for "Split PDF" functionality (#1025)
* Update screen of homepage

* Fix description for "Split PDF" except AR

* Fix description for "Split PDF" in AR

* Fix one mistake on AR

* Keep clear this branch from other branches
2024-04-03 22:40:35 +01:00
Dimitris Doukas
5d71ffbfaa Update homepage screen in README (#1021)
Update screen of homepage
2024-04-03 22:40:09 +01:00
CocoMaster-AI
c0c137d1b0 Sponsor information supports translate now (#1023)
Sponsored information supports translate now
2024-04-03 10:02:01 +01:00
Dimitris Doukas
a05cfd52cb Merge branch 'updatereadme' 2024-04-02 21:01:07 +03:00
Dimitris Doukas
ad0967f7d0 Update screen of homepage 2024-04-02 12:50:53 +03:00
Anthony Stirling
8625db2885 Update build.gradle 2024-04-01 22:10:33 +01:00
Anthony Stirling
a2a969a0a0 Chinese font and word conv fix (#1014)
* remove lite package

* alphine update for word conversion issue #995
2024-04-01 22:04:27 +01:00
Anthony Stirling
8a6386ca73 remove lite package (#1012) 2024-04-01 18:33:58 +01:00
NicolasFR
255c018415 Update messages_fr_FR.properties (#1011)
fix(lang): fix a translation error
2024-04-01 17:55:22 +01:00
albanobattistella
df27ed6907 Update messages_it_IT.properties (#1010) 2024-04-01 15:04:05 +01:00
CocoMaster-AI
989491b903 Extract text from error pages and sync text variable to all lang (#1008)
* feat: extract text from error pages

* feat: sync text variables to langs.properties

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-01 11:04:56 +01:00
NicolasFR
664dd62d8b doc: add --break-system-packages (#1001)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-04-01 10:56:33 +01:00
albanobattistella
3d6b145db5 Update messages_it_IT.properties (#1002) 2024-03-30 19:51:44 +00:00
CocoMaster-AI
37a63242a6 Update messages_ru_RU.properties (#999) 2024-03-30 10:26:51 +00:00
Eric
dfb8c64f5a fix: switch to pdftohtml for pdf to html conversions (#998)
* fix: switch to pdftohtml for pdf to html conversions

* build: include poppler-utils in dockerfile for pdftohtml
2024-03-29 17:02:33 -04:00
github-actions[bot]
27bbf7a513 💾 Update Version (#997)
💾 Sync Versions
> Made via sync_versions.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-03-29 11:04:05 +00:00
Anthony Stirling
ca890e4b32 Pipeline fix quick 2024-03-29 11:03:13 +00:00
CocoMaster-AI
4834d01223 feat: translate messages_ko_KR.properties, messages_ru_RU.properties completed (#996)
* feat: translate ko_KR.properties completed

* feat: feat: translate ru_RU.properties completed
2024-03-29 10:17:10 +00:00
albanobattistella
84b3bb1aed Update messages_it_IT.properties (#993) 2024-03-28 20:50:29 +00:00
Anthony Stirling
a9679da719 Revert weasy 2024-03-28 19:38:56 +00:00
Anthony Stirling
f10b3ffe3c multiTool.uploadPrompts 2024-03-28 17:45:58 +00:00
SuperFan
ea982d6412 fix: Add MultiTool UploadPrompts zh_CN And en_US (#977)
Co-authored-by: superfan <admin@henniubi.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-03-28 17:44:21 +00:00
pixeebot[bot]
1035a3be31 Define a constant for a literal string that is duplicated n times (Sonar) (#978)
Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2024-03-28 17:44:10 +00:00
github-actions[bot]
c16db14cd9 Update 3rd Party Licenses (#992)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-03-28 17:43:30 +00:00
github-actions[bot]
1698f9d5df 💾 Update Version (#991)
💾 Sync Versions
> Made via sync_versions.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-03-28 17:43:20 +00:00
Anthony Stirling
08e43cc89c fix #986 and #989 2024-03-28 17:09:21 +00:00
NicolasFR
fb1baaa275 fix: Fix package name of openjdk for debian (#990) 2024-03-28 16:53:54 +00:00
CocoMaster-AI
eda838d6f8 feat: Extract translation text from html and finished the mainly translation of zh_CN, zh_TW, ja_JP file (#987)
* feat: translate zh_cn properties

* feat: adjust some description messages_zh_CN.properties

* feat: translate zh_TW file done

* feat: zh_CN semantic optimization

* feat: Supplementary ja_JP translation

* feat: extract rest text

* feat: Sync to all lang.properties

* feat: translate zh_CN, zh_TW, ja_JP file

* fix: small bug

---------

Co-authored-by: yanyu_lin <yanyu_lin@bestsign.cn>
2024-03-28 16:53:39 +00:00
Anthony Stirling
2fff3083ae Update TextFinder.java (#980) 2024-03-26 19:25:16 +00:00
tkymmm
7e2d58b3e8 Update messages_ja_JP.properties (#975) 2024-03-26 06:55:51 +00:00
NeilJared
31ec385282 Update messages_es_ES.properties (#969)
Updated es_ES translation (100% done)
2024-03-25 19:07:53 +00:00
Anthony Stirling
14ef7c0a72 dummy commit (#968)
Update README.md
2024-03-24 10:16:14 +00:00
Anthony Stirling
c9331afeac github bug 2024-03-22 20:15:53 +00:00
Anthony Stirling
09cb92e235 github bug 2024-03-22 20:15:42 +00:00
Anthony Stirling
6bd6e6563b Update Chart.yaml 2024-03-22 20:01:37 +00:00
Anthony Stirling
3c08c20426 Update build.gradle 2024-03-22 19:57:43 +00:00
Anthony Stirling
3800e3e465 Update view-pdf.html 2024-03-22 18:15:10 +00:00
github-actions[bot]
e2bd73dbf3 Update 3rd Party Licenses (#966)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-03-21 21:42:34 +00:00
github-actions[bot]
a20c3018ae Update 3rd Party Licenses (#965)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-03-21 21:41:09 +00:00
dependabot[bot]
7f17b33859 Bump org.apache.pdfbox:pdfbox from 3.0.1 to 3.0.2 (#963)
Bumps org.apache.pdfbox:pdfbox from 3.0.1 to 3.0.2.

---
updated-dependencies:
- dependency-name: org.apache.pdfbox:pdfbox
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:40:59 +00:00
dependabot[bot]
029937a1c5 Bump org.springframework.boot:spring-boot-starter-test from 3.2.3 to 3.2.4 (#961)
Bump org.springframework.boot:spring-boot-starter-test

Bumps [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-test
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:24:20 +00:00
dependabot[bot]
cfcf02708c Bump org.springframework.boot from 3.2.3 to 3.2.4 (#962)
Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:24:15 +00:00
dependabot[bot]
c1724ef74c Bump org.springdoc:springdoc-openapi-starter-webmvc-ui from 2.2.0 to 2.4.0 (#964)
Bump org.springdoc:springdoc-openapi-starter-webmvc-ui

Bumps [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) from 2.2.0 to 2.4.0.
- [Release notes](https://github.com/springdoc/springdoc-openapi/releases)
- [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md)
- [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.2.0...v2.4.0)

---
updated-dependencies:
- dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:24:06 +00:00
github-actions[bot]
3c53f97c36 Update 3rd Party Licenses (#960)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-03-21 21:17:53 +00:00
dependabot[bot]
aa895d10ac Bump org.springframework.boot:spring-boot-starter-thymeleaf from 3.2.3 to 3.2.4 (#954)
Bump org.springframework.boot:spring-boot-starter-thymeleaf

Bumps [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:13:55 +00:00
dependabot[bot]
6c603618ce Bump org.springframework.boot:spring-boot-starter-security from 3.2.3 to 3.2.4 (#956)
Bump org.springframework.boot:spring-boot-starter-security

Bumps [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-security
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:12:33 +00:00
dependabot[bot]
78aa0d4c61 Bump org.springframework.boot:spring-boot-starter-data-jpa from 3.2.3 to 3.2.4 (#953)
Bump org.springframework.boot:spring-boot-starter-data-jpa

Bumps [org.springframework.boot:spring-boot-starter-data-jpa](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-data-jpa
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:10:56 +00:00
dependabot[bot]
da3fc72e5c Bump org.projectlombok:lombok from 1.18.28 to 1.18.32 (#955)
Bumps [org.projectlombok:lombok](https://github.com/projectlombok/lombok) from 1.18.28 to 1.18.32.
- [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown)
- [Commits](https://github.com/projectlombok/lombok/compare/v1.18.28...v1.18.32)

---
updated-dependencies:
- dependency-name: org.projectlombok:lombok
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:09:30 +00:00
dependabot[bot]
a800766cb8 Bump org.commonmark:commonmark from 0.21.0 to 0.22.0 (#957)
Bumps [org.commonmark:commonmark](https://github.com/commonmark/commonmark-java) from 0.21.0 to 0.22.0.
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.21.0...commonmark-parent-0.22.0)

---
updated-dependencies:
- dependency-name: org.commonmark:commonmark
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 21:08:00 +00:00
Ludy
67a1529dc7 Change to html5 (#958)
* Change to html5

with Nu Html Checker

* Update scale-pages.html

* Update sign.html

* Update common.html

* Update common.html

* Update login.html
2024-03-21 20:58:01 +00:00
github-actions[bot]
77354f47bf Update 3rd Party Licenses (#952)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-03-21 20:12:47 +00:00
dependabot[bot]
49ea07fd13 Bump io.github.pixee:java-security-toolkit from 1.1.2 to 1.1.3 (#946)
Bumps [io.github.pixee:java-security-toolkit](https://github.com/pixee/java-security-toolkit) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/pixee/java-security-toolkit/releases)
- [Commits](https://github.com/pixee/java-security-toolkit/compare/v1.1.2...v1.1.3)

---
updated-dependencies:
- dependency-name: io.github.pixee:java-security-toolkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 20:09:55 +00:00
dependabot[bot]
bb69c67b52 Bump org.springframework.boot:spring-boot-starter-actuator from 3.2.3 to 3.2.4 (#950)
Bump org.springframework.boot:spring-boot-starter-actuator

Bumps [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-actuator
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 20:09:37 +00:00
dependabot[bot]
6b29c28e2e Bump org.springframework.boot:spring-boot-devtools from 3.2.3 to 3.2.4 (#949)
Bumps [org.springframework.boot:spring-boot-devtools](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-devtools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 20:09:32 +00:00
dependabot[bot]
ba0fe43f31 Bump org.springframework.boot:spring-boot-starter-web from 3.2.3 to 3.2.4 (#948)
Bump org.springframework.boot:spring-boot-starter-web

Bumps [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.2.3...v3.2.4)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-web
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 20:09:23 +00:00
dependabot[bot]
e54597f108 Bump com.github.jk1.dependency-license-report from 2.5 to 2.6 (#932)
Bumps com.github.jk1.dependency-license-report from 2.5 to 2.6.

---
updated-dependencies:
- dependency-name: com.github.jk1.dependency-license-report
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 20:09:10 +00:00
dependabot[bot]
9809ad9d7b Bump io.micrometer:micrometer-core from 1.12.3 to 1.12.4 (#930)
Bumps [io.micrometer:micrometer-core](https://github.com/micrometer-metrics/micrometer) from 1.12.3 to 1.12.4.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.12.3...v1.12.4)

---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 19:36:45 +00:00
dependabot[bot]
1d4ad19acd Bump org.springframework:spring-webmvc from 6.1.4 to 6.1.5 (#934)
Bumps [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) from 6.1.4 to 6.1.5.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.4...v6.1.5)

---
updated-dependencies:
- dependency-name: org.springframework:spring-webmvc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 19:36:12 +00:00
dependabot[bot]
2c24e754be Bump org.apache.pdfbox:xmpbox from 3.0.1 to 3.0.2 (#933)
Bumps org.apache.pdfbox:xmpbox from 3.0.1 to 3.0.2.

---
updated-dependencies:
- dependency-name: org.apache.pdfbox:xmpbox
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 19:35:55 +00:00
albanobattistella
4a3326a560 Update messages_it_IT.properties (#927) 2024-03-17 14:24:07 +00:00
Ludy
24862e2d4a changes characters in multiple languages - Unicode ascii (#925)
* unicode to readable font

* Update messages_de_DE.properties
2024-03-17 11:09:55 +00:00
Ludy
51bb26ae34 Fix: Error after logging out and selecting another language (#924)
Fix exception url after logout
2024-03-17 09:57:17 +00:00
IT Creativity + Art Team
7e4d8f45f6 Merge branch 'Stirling-Tools:main' into main 2024-02-14 21:26:22 +02:00
Stirling-PDF-Bot
9dbc2712e7 Update 3rd Party Licenses 2024-01-16 20:06:35 +00:00
302 changed files with 15717 additions and 6421 deletions

View File

@@ -2,12 +2,12 @@ name: "Build repo"
on:
push:
branches: [ "main" ]
branches: ["main"]
paths-ignore:
- ".github/**"
- "**/*.md"
pull_request:
branches: [ "main" ]
branches: ["main"]
paths-ignore:
- ".github/**"
- "**/*.md"
@@ -25,16 +25,18 @@ jobs:
fail-fast: false
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- uses: gradle/gradle-build-action@v2.4.2
with:
gradle-version: 7.6
arguments: build --no-build-cache
- uses: gradle/actions/setup-gradle@v3
with:
gradle-version: 7.6
- name: Build with Gradle
run: ./gradlew build --no-build-cache

View File

@@ -5,7 +5,7 @@ on:
branches:
- main
paths:
- 'build.gradle'
- "build.gradle"
permissions:
contents: write
@@ -17,13 +17,15 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'adopt'
java-version: "17"
distribution: "adopt"
- uses: gradle/actions/setup-gradle@v3
- name: Run Gradle Command
run: ./gradlew clean generateLicenseReport
@@ -44,7 +46,7 @@ jobs:
- name: Create Pull Request
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@v3
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Update 3rd Party Licenses"
@@ -58,4 +60,3 @@ jobs:
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true

View File

@@ -6,9 +6,6 @@ on:
branches:
- master
- main
paths-ignore:
- ".github/**"
- "**/*.md"
permissions:
contents: read
@@ -17,139 +14,99 @@ jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3.5.2
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Set up JDK 17
uses: actions/setup-java@v3.11.0
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
with:
gradle-version: 7.6
- name: Run Gradle Command
run: ./gradlew clean build
env:
DOCKER_ENABLE_SECURITY: false
- uses: gradle/gradle-build-action@v2.4.2
env:
DOCKER_ENABLE_SECURITY: false
with:
gradle-version: 7.6
arguments: clean build
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Make Gradle wrapper executable
run: chmod +x gradlew
- name: Get version number
id: versionNumber
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
- name: Get version number
id: versionNumber
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_API }}
- name: Login to Docker Hub
uses: docker/login-action@v2.1.0
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_API }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Convert repository owner to lowercase
id: repoowner
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
- name: Convert repository owner to lowercase
id: repoowner
run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT
- name: Generate tags
id: meta
uses: docker/metadata-action@v4.4.0
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
- name: Generate tags
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
- name: Build and push main Dockerfile
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.5.0
- name: Generate tags ultra-lite
id: meta2
uses: docker/metadata-action@v5
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
- name: Build and push main Dockerfile
uses: docker/build-push-action@v4.0.0
with:
context: .
dockerfile: ./Dockerfile
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args:
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8
- name: Generate tags ultra-lite
id: meta2
uses: docker/metadata-action@v4.4.0
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
- name: Build and push Dockerfile-ultra-lite
uses: docker/build-push-action@v4.0.0
if: github.ref != 'refs/heads/main'
with:
context: .
file: ./Dockerfile-ultra-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta2.outputs.tags }}
labels: ${{ steps.meta2.outputs.labels }}
build-args:
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8
- name: Generate tags lite
id: meta3
uses: docker/metadata-action@v4.4.0
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-lite,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-lite,enable=${{ github.ref == 'refs/heads/master' }}
- name: Build and push Dockerfile-lite
uses: docker/build-push-action@v4.0.0
if: github.ref != 'refs/heads/main'
with:
context: .
file: ./Dockerfile-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta3.outputs.tags }}
labels: ${{ steps.meta3.outputs.labels }}
build-args:
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8
- name: Build and Push Helm Chart
run: |
helm package chart/stirling-pdf
helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle
- name: Build and push Dockerfile-ultra-lite
uses: docker/build-push-action@v5
if: github.ref != 'refs/heads/main'
with:
context: .
file: ./Dockerfile-ultra-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta2.outputs.tags }}
labels: ${{ steps.meta2.outputs.labels }}
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8

View File

@@ -1,6 +1,7 @@
name: Release Artifacts
on:
workflow_dispatch:
release:
types: [created]
permissions:
@@ -14,44 +15,61 @@ jobs:
enable_security: [true, false]
include:
- enable_security: true
file_suffix: '-with-login'
file_suffix: "-with-login"
- enable_security: false
file_suffix: ''
file_suffix: ""
steps:
- uses: actions/checkout@v3.5.2
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v3.11.0
with:
java-version: '17'
distribution: 'temurin'
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- uses: gradle/actions/setup-gradle@v3
with:
gradle-version: 7.6
- name: Generate jar (With Security=${{ matrix.enable_security }})
run: ./gradlew clean createExe
env:
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
- name: Generate jar (With Security=${{ matrix.enable_security }})
run: ./gradlew clean createExe
env:
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./build/launch4j/Stirling-PDF.exe
asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe
tag: ${{ github.ref }}
overwrite: true
- name: Get version number
id: versionNumber
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
- name: Get version number
id: versionNumber
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
- name: Rename binarie
if: matrix.file_suffix != ''
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
- name: Upload jar binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar
asset_name: Stirling-PDF${{ matrix.file_suffix }}.jar
tag: ${{ github.ref }}
overwrite: true
- name: Upload Assets binarie
uses: actions/upload-artifact@v4
with:
path: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
name: Stirling-PDF${{ matrix.file_suffix }}.exe
overwrite: true
retention-days: 1
if-no-files-found: error
- name: Upload binaries to release
uses: softprops/action-gh-release@v2
with:
files: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
- name: Rename jar binaries
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
- name: Upload Assets jar binaries
uses: actions/upload-artifact@v4
with:
path: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
name: Stirling-PDF${{ matrix.file_suffix }}.jar
overwrite: true
retention-days: 1
if-no-files-found: error
- name: Upload jar binaries to release
uses: softprops/action-gh-release@v2
with:
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar

View File

@@ -5,36 +5,35 @@ on:
push:
branches:
- master
paths-ignore:
- ".github/**"
- "**/*.md"
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3.5.2
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Set up JDK 17
uses: actions/setup-java@v3.11.0
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Generate Swagger documentation
run: ./gradlew generateOpenApiDocs
- name: Generate Swagger documentation
run: ./gradlew generateOpenApiDocs
- name: Upload Swagger Documentation to SwaggerHub
run: ./gradlew swaggerhubUpload
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
- name: Upload Swagger Documentation to SwaggerHub
run: ./gradlew swaggerhubUpload
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
- name: Get version number
id: versionNumber
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
- name: Set API version as published and default on SwaggerHub
run: |
curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}"
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
- name: Set API version as published and default on SwaggerHub
run: |
curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}"
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}

90
.github/workflows/sync_files.yml vendored Normal file
View File

@@ -0,0 +1,90 @@
name: Sync Files
on:
push:
branches:
- main
paths:
- "build.gradle"
- "src/main/resources/messages_*.properties"
- "scripts/translation_status.toml"
permissions:
contents: write
pull-requests: write
jobs:
sync-versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
- name: Set up Python
uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Install dependencies
run: pip install pyyaml
- name: Sync versions
run: python .github/scripts/gradle_to_chart.py
- name: Set up git config
run: |
git config --global user.email "GitHub Action <action@github.com>"
git config --global user.name "GitHub Action <action@github.com>"
- name: Run git add
run: |
git add .
git diff --staged --quiet || git commit -m ":floppy_disk: Sync Versions
> Made via sync_files.yml" || echo "no changes"
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6.0.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update files
committer: GitHub Action <action@github.com>
author: GitHub Action <action@github.com>
signoff: true
branch: sync_version
title: ":floppy_disk: Update Version"
body: |
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true
sync-readme:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
- name: Set up Python
uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Install dependencies
run: pip install tomlkit
- name: Sync README
run: python scripts/counter_translation.py
- name: Set up git config
run: |
git config --global user.email "GitHub Action <action@github.com>"
git config --global user.name "GitHub Action <action@github.com>"
- name: Run git add
run: |
git add .
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@v6.0.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update files
committer: GitHub Action <action@github.com>
author: GitHub Action <action@github.com>
signoff: true
branch: sync_readme
title: ":memo: Update README: Translation Progress Table"
body: |
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true

View File

@@ -1,51 +0,0 @@
name: Sync Versions
on:
push:
branches:
- main
paths:
- "build.gradle"
permissions:
contents: write
pull-requests: write
jobs:
sync-versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
- name: Set up Python
uses: actions/setup-python@v5.0.0
with:
python-version: '3.x'
- name: Install dependencies
run: pip install pyyaml
- name: Sync versions
run: python .github/scripts/gradle_to_chart.py
- name: Set up git config
run: |
git config --global user.email "GitHub Action <action@github.com>"
git config --global user.name "GitHub Action <action@github.com>"
- name: Run git add
run: |
git add .
git diff --staged --quiet || git commit -m ":floppy_disk: Sync Versions
> Made via sync_versions.yml" || echo "no changes"
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update files
committer: GitHub Action <action@github.com>
author: GitHub Action <action@github.com>
signoff: true
branch: sync_version
title: ":floppy_disk: Update Version"
body: |
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true

View File

@@ -3,54 +3,36 @@ name: Docker Compose Tests
on:
pull_request:
paths:
- 'src/**'
- '**.gradle'
- '!src/main/java/resources/messages*'
- 'exampleYmlFiles/**'
- 'Dockerfile'
- 'Dockerfile**'
- "src/**"
- "**.gradle"
- "!src/main/java/resources/messages*"
- "exampleYmlFiles/**"
- "Dockerfile"
- "Dockerfile**"
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'adopt'
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "adopt"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Run Docker Compose Tests
run: |
chmod +x ./gradlew
- name: Install Docker Compose
run: |
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.26.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# sudo chmod +x /usr/local/bin/docker-compose
- name: Get version number
id: versionNumber
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ steps.versionNumber.outputs.versionNumber }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Install Docker Compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- name: Run Docker Compose Tests
run: |
chmod +x ./test.sh
./test.sh
- name: Run Docker Compose Tests
run: |
chmod +x ./test.sh
./test.sh

View File

@@ -27,6 +27,10 @@ Please make sure your Pull Request adheres to the following guidelines:
If you would like to add or modify a translation, please see [How to add new languages to Stirling-PDF](HowToAddNewLanguage.md). Also, please create a Pull Request so others can use it!
## Docs
Documentation for Stirling-PDF is handled in a seperate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use "edit this page"-button at the bottom of each page at [https://stirlingtools.com/docs/](https://stirlingtools.com/docs/).
## Fixing Bugs or Adding a New Feature
First, make sure you've read the section [Pull Requests](#pull-requests).

View File

@@ -1,11 +1,11 @@
# Main stage
FROM alpine:3.19.1
FROM alpine:20240329
# Copy necessary files
COPY scripts /scripts
COPY pipeline /pipeline
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
#COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
COPY build/libs/*.jar app.jar
ARG VERSION_TAG
@@ -25,17 +25,22 @@ ENV DOCKER_ENABLE_SECURITY=false \
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk update && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
openssl \
openssl-dev \
bash \
curl \
openjdk17-jre \
openjdk21-jre \
su-exec \
shadow \
# Doc conversion
libreoffice@testing \
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced featues)
ocrmypdf \
tesseract-ocr-data-eng \
@@ -54,7 +59,9 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar
chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
tesseract --list-langs && \
rm -rf /var/cache/apk/*
EXPOSE 8080

View File

@@ -1,63 +0,0 @@
# use alpine
FROM alpine:3.19.1
ARG VERSION_TAG
# Set Environment Variables
ENV DOCKER_ENABLE_SECURITY=false \
HOME=/home/stirlingpdfuser \
VERSION_TAG=$VERSION_TAG \
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
PUID=1000 \
PGID=1000 \
UMASK=022
# Copy necessary files
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
COPY pipeline /pipeline
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
COPY build/libs/*.jar app.jar
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
bash \
curl \
gcc \
openjdk17-jre \
su-exec \
shadow \
# Doc conversion
libreoffice@testing \
# python and pip
python3 && \
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
# uno unoconv and HTML
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
# Set up necessary directories and permissions
mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
# Set font cache and permissions
fc-cache -f -v && \
chmod +x /scripts/*.sh && \
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar
# Set environment variables
ENV ENDPOINTS_GROUPS_TO_REMOVE=OpenCV,OCRmyPDF
ENV DOCKER_ENABLE_SECURITY=false
EXPOSE 8080
# Run the application
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]

View File

@@ -31,7 +31,7 @@ RUN mkdir /configs /logs /customFiles && \
curl \
su-exec \
shadow \
openjdk17-jre && \
openjdk21-jre && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \

View File

@@ -34,5 +34,18 @@ Then simply translate all property entries within that file and make a PR into m
If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but won't be able to verify the translations themselves)
## Handling Untranslatable Strings
Sometimes, certain strings in the properties file may not require translation because they are the same in the target language or are universal (like names of protocols, certain terminologies, etc.). To ensure accurate statistics for language progress, these strings should be added to the `ignore_translation.toml` file located in the `scripts` directory. This will exclude them from the translation progress calculations.
For example, if the English string error=Error does not need translation in Polish, add it to the ignore_translation.toml under the Polish section:
```toml
[pl_PL]
ignore = [
"language.direction", # Existing entries
"error" # Add new entries here
]
```
Make sure to place the entry under the correct language section. This helps maintain the accuracy of translation progress statistics and ensures that the translation tool or scripts do not misinterpret the completion rate.

View File

@@ -14,7 +14,7 @@ You could theoretically use a Distrobox/Toolbox, if your Distribution has old or
Install the following software, if not already installed:
- Java 17 or later
- Java 17 or later (21 recommended)
- Gradle 7.0 or later (included within repo so not needed on server)
@@ -42,17 +42,25 @@ 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++ java-17-openjdk python3 python3-pip
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-17-openjdk python3 python3-pip
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 &&\
@@ -64,6 +72,11 @@ 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, ocrmypdf for OCR, and opencv for pattern recognition functionality.
@@ -95,7 +108,7 @@ For Debian-based systems, you can use the following command:
```bash
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages
```
For Fedora:
@@ -105,6 +118,13 @@ sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpa
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
```
For Nix:
```bash
nix-env -iA nixpkgs.unpaper nixpkgs.libreoffice nixpkgs.ocrmypdf nixpkgs.poppler_utils
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
```
### Step 4: Clone and Build Stirling-PDF
```bash
@@ -115,13 +135,12 @@ 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
This folder is required for the python scripts using OpenCV.
```bash
sudo mkdir /opt/Stirling-PDF &&\
@@ -129,19 +148,25 @@ 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
Easiest is to use the langpacks provided by your repositories. Skip the other steps
Easiest is to use the langpacks provided by your repositories. Skip the other steps.
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`
3.
Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
3. Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
Debian based systems, install languages with this command:
@@ -171,14 +196,38 @@ dnf search -C tesseract-langpack-
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 the 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 Appstarter to your Appmenu.
@@ -202,7 +251,19 @@ EOF
Note: Currently the app will run in the background until manually closed.
### Optional: Run Stirling-PDF as a service
### 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:
```bash
server:
host: 0.0.0.0
port: 3000
```
**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:
```
@@ -239,6 +300,7 @@ 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):
```
sudo systemctl daemon-reload
```

145
README.md
View File

@@ -1,34 +1,35 @@
<p align="center"><img src="https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/stirling.png" width="80" ><br><h1 align="center">Stirling-PDF</h1>
</p>
<p align="center"><img src="https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/stirling.png" width="80" ></p>
<h1 align="center">Stirling-PDF</h1>
[![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/Cn8pWhQRxZ)
[![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://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)
[![Paypal Donate](https://img.shields.io/badge/Paypal%20Donate-yellow?style=flat&logo=paypal)](https://www.paypal.com/paypalme/froodleplex)
[![Paypal Donate](https://img.shields.io/badge/Paypal%20Donate-yellow?style=flat&logo=paypal)](https://www.paypal.com/donate/?hosted_button_id=MN7JPG5G6G3JL)
[![Github Sponsor](https://img.shields.io/badge/Github%20Sponsor-yellow?style=flat&logo=github)](https://github.com/sponsors/Frooodle)
[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
This is a powerful locally hosted web based PDF manipulation tool using docker that allows you to perform various operations on PDF files, such as splitting merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application started as a 100% ChatGPT-made application and has evolved to include a wide range of features to handle all your PDF needs.
This is a robust, locally hosted web-based PDF manipulation tool using Docker. It enables you to carry out various operations on PDF files, including splitting, merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
Stirling PDF makes no outbound calls for any record keeping or tracking.
Stirling PDF does not initiate any outbound calls for record-keeping or tracking purposes.
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.
![stirling-home](images/stirling-home.png)
![stirling-home](images/stirling-home.jpg)
## Features
- Dark mode support.
- Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example)
- Parallel file processing and downloads
- 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)
## **PDF Features**
### **Page Operations**
- View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts)
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
- Merge multiple PDFs together into a single resultant file.
@@ -45,6 +46,7 @@ All files and PDFs exist either exclusively on the client side, reside in server
- Convert PDF to a single page.
### **Conversion Operations**
- Convert PDFs to and from images.
- Convert any common file to PDF (using LibreOffice).
- Convert PDF to Word/Powerpoint/Others (using LibreOffice).
@@ -53,6 +55,7 @@ All files and PDFs exist either exclusively on the client side, reside in server
- Markdown to PDF.
### **Security & Permissions**
- Add and remove passwords.
- Change/set PDF Permissions.
- Add watermark(s).
@@ -61,6 +64,7 @@ All files and PDFs exist either exclusively on the client side, reside in server
- Auto-redact text.
### **Other Operations**
- Add/Generate/Write signatures.
- Repair PDFs.
- Detect and remove blank pages.
@@ -77,11 +81,11 @@ All files and PDFs exist either exclusively on the client side, reside in server
- Flatten PDFs.
- Get all information on a PDF to view or export as JSON.
For a 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)
Demo of the app is available [here](https://stirlingpdf.io). username: demo, password: demo
## Technologies used
- Spring Boot + Thymeleaf
- [PDFBox](https://github.com/apache/pdfbox/tree/trunk)
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
@@ -94,19 +98,21 @@ Demo of the app is available [here](https://stirlingpdf.io). username: demo, pas
## How to use
### Locally
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/LocalRunGuide.md
### Docker / Podman
https://hub.docker.com/r/frooodle/s-pdf
Stirling PDF has 3 different versions, a Full version, Lite, and ultra-Lite. Depending on the types of features you use you may want a smaller image to save on space.
Stirling PDF has 2 different versions, a Full version and ultra-Lite 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 that don't mind about space optimization just use the latest tag.
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest?label=Stirling-PDF%20Full)
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-lite?label=Stirling-PDF%20Lite)
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-ultra-lite?label=Stirling-PDF%20Ultra-Lite)
Docker Run
```bash
docker run -d \
-p 8080:8080 \
@@ -115,6 +121,7 @@ docker run -d \
-v /location/of/logs:/logs \
-e DOCKER_ENABLE_SECURITY=false \
-e INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
-e LANGS=en_GB \
--name stirling-pdf \
frooodle/s-pdf:latest
@@ -123,7 +130,9 @@ docker run -d \
-v /location/of/customFiles:/customFiles \
```
Docker Compose
```yaml
version: '3.3'
services:
@@ -139,59 +148,68 @@ services:
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".
## Enable OCR/Compression feature
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md
## Supported Languages
Stirling PDF currently supports 26!
- English (English) (en_GB)
- English (US) (en_US)
- Arabic (العربية) (ar_AR)
- German (Deutsch) (de_DE)
- French (Français) (fr_FR)
- Spanish (Español) (es_ES)
- Simplified Chinese (简体中文) (zh_CN)
- Traditional Chinese (繁體中文) (zh_TW)
- Catalan (Català) (ca_CA)
- Italian (Italiano) (it_IT)
- Swedish (Svenska) (sv_SE)
- Polish (Polski) (pl_PL)
- Romanian (Română) (ro_RO)
- Korean (한국어) (ko_KR)
- Portuguese Brazilian (Português) (pt_BR)
- Russian (Русский) (ru_RU)
- Basque (Euskara) (eu_ES)
- Japanese (日本語) (ja_JP)
- Dutch (Nederlands) (nl_NL)
- Greek (el_GR)
- Turkish (Türkçe) (tr_TR)
- Indonesia (Bahasa Indonesia) (id_ID)
- Hindi (हिंदी) (hi_IN)
- Hungarian (Magyar) (hu_HU)
- Bulgarian (Български) (bg_BG)
- Sebian Latin alphabet (Srpski) (sr_LATN_RS)
Stirling PDF currently supports 27!
| Language | Progress |
| ------------------------------------------- | -------------------------------------- |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| Arabic (العربية) (ar_AR) | ![41%](https://geps.dev/progress/41) |
| German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
| French (Français) (fr_FR) | ![94%](https://geps.dev/progress/94) |
| Spanish (Español) (es_ES) | ![97%](https://geps.dev/progress/97) |
| Simplified Chinese (简体中文) (zh_CN) | ![96%](https://geps.dev/progress/96) |
| Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) |
| Catalan (Català) (ca_CA) | ![50%](https://geps.dev/progress/50) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Swedish (Svenska) (sv_SE) | ![41%](https://geps.dev/progress/41) |
| Polish (Polski) (pl_PL) | ![43%](https://geps.dev/progress/43) |
| Romanian (Română) (ro_RO) | ![40%](https://geps.dev/progress/40) |
| Korean (한국어) (ko_KR) | ![89%](https://geps.dev/progress/89) |
| Portuguese Brazilian (Português) (pt_BR) | ![62%](https://geps.dev/progress/62) |
| Russian (Русский) (ru_RU) | ![89%](https://geps.dev/progress/89) |
| Basque (Euskara) (eu_ES) | ![65%](https://geps.dev/progress/65) |
| Japanese (日本語) (ja_JP) | ![89%](https://geps.dev/progress/89) |
| Dutch (Nederlands) (nl_NL) | ![86%](https://geps.dev/progress/86) |
| Greek (Ελληνικά) (el_GR) | ![87%](https://geps.dev/progress/87) |
| Turkish (Türkçe) (tr_TR) | ![99%](https://geps.dev/progress/99) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![80%](https://geps.dev/progress/80) |
| Hindi (हिंदी) (hi_IN) | ![81%](https://geps.dev/progress/81) |
| Hungarian (Magyar) (hu_HU) | ![79%](https://geps.dev/progress/79) |
| Bulgarian (Български) (bg_BG) | ![96%](https://geps.dev/progress/96) |
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![82%](https://geps.dev/progress/82) |
| Ukrainian (Українська) (uk_UA) | ![88%](https://geps.dev/progress/88) |
| Slovakian (Slovensky) (sk_SK) | ![96%](https://geps.dev/progress/96) |
## Contributing (creating issues, translations, fixing bugs, etc.)
Please see our [Contributing Guide](CONTRIBUTING.md)!
## Customisation
Stirling PDF allows easy customization of the app.
Includes things like
- Custom application name
- Custom slogans, icons, images, and even custom HTML (via file overrides)
- 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``
This file is located in the ``/configs`` directory and follows standard YAML formatting
Environment variables are also supported and would override the settings file
For example in the settings.yml you have
```yaml
system:
defaultLocale: 'en-US'
@@ -200,48 +218,75 @@ system:
To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE``
The Current list of settings is
```yaml
security:
enableLogin: false # set to 'true' to enable login
csrfDisabled: true
csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production)
loginAttemptCount: 5 # lock user account after 5 tries
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
# initialLogin:
# username: "admin" # Initial username for the first login (these are defaulted)
# password: "stirling" # 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)
# issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point
# 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
# 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'
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
customStaticFilePath: '/customFiles/static/' # Directory path for custom static files
enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes)
showUpdate: true # 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
#ui:
# appName: exampleAppName # Application's visible name
# homeDescription: I am a description # Short description or tagline shown on homepage.
# appNameNavbar: navbarName # Name displayed on the navigation bar
ui:
appName: null # Application's visible name
homeDescription: null # Short description or tagline shown on homepage.
appNameNavbar: null # 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 endpoints (view http://localhost:8080/swagger-ui/index.html#/API to learn more), 'false' to disable
enabled: true # 'true' to enable Info APIs (`/api/*`) endpoints, 'false' to disable
```
There is an additional config file ``/configs/custom_settings.yml`` were users familiar with java and spring application.properties can input their own settings on-top of Stirling-PDFs existing ones
### Extra notes
- Endpoints. Currently, the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma separate lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image-to-pdf and remove pages, 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. Customise static files such as the app logo by placing files in the /customFiles/static/ directory. An example of customising app logo is placing a /customFiles/static/favicon.svg to override current SVG. This can be used to change any images/icons/css/fonts/js etc in Stirling-PDF
### Environment only parameters
- ``SYSTEM_ROOTURIPATH`` ie set to ``/pdf-app`` to Set the application's root URI to ``localhost:8080/pdf-app``
- ``SYSTEM_CONNECTIONTIMEOUTMINUTES`` to set custom connection timeout values
- ``DOCKER_ENABLE_SECURITY`` to tell docker to download security jar (required as true for auth login)
- ``INSTALL_BOOK_AND_ADVANCED_HTML_OPS`` to download calibre onto stirling-pdf enabling pdf to/from book and advanced html conversion
- ``LANGS`` to define custom font libraries to install for use for document conversions
## API
For those wanting to use Stirling-PDFs 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 versions documentation (Or by following the API button in your 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 uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables.
- 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 via setting ``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 straight away (Recommended to remove them after user creation).
@@ -255,10 +300,10 @@ To add new users go to the bottom of Account settings and hit 'Admin Settings',
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
@@ -268,7 +313,9 @@ For API usage you must provide a header with 'X-API-Key' and the associated API
- Fill forms manually or automatically
### Q2: Why is my application downloading .htm files?
This is an issue caused commonly 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;``

View File

@@ -1,64 +1,52 @@
|Technology | Ultra-Lite | Lite | Full |
|----------------|:----------:|:----:|:----:|
| Java | ✔️ | ✔️ | ✔️ |
| JavaScript | ✔️ | ✔️ | ✔️ |
| Libre | | ✔️ | ✔️ |
| Python | | | ✔️ |
| OpenCV | | | ✔️ |
| OCRmyPDF | | | ✔️ |
| Technology | Ultra-Lite | Full |
|----------------|:----------:|:----:|
| Java | ✔️ | ✔️ |
| JavaScript | ✔️ | ✔️ |
| Libre | | ✔️ |
| Python | | ✔️ |
| OpenCV | | ✔️ |
| OCRmyPDF | | ✔️ |
Operation | Ultra-Lite | Lite | Full
--------------------|------------|------|-----
add-page-numbers | ✔️ | ✔️ | ✔️
add-password | ✔️ | ✔️ | ✔️
add-image | ✔️ | ✔️ | ✔️
add-watermark | ✔️ | ✔️ | ✔️
adjust-contrast | ✔️ | ✔️ | ✔️
auto-split-pdf | ✔️ | ✔️ | ✔️
auto-redact | ✔️ | ✔️ | ✔️
auto-rename | ✔️ | ✔️ | ✔️
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 | ✔️ | ✔️ | ✔️
file-to-pdf | | ✔️ | ✔️
pdf-to-html | | ✔️ | ✔️
pdf-to-presentation | | ✔️ | ✔️
pdf-to-text | | ✔️ | ✔️
pdf-to-word | | ✔️ | ✔️
pdf-to-xml | | ✔️ | ✔️
repair | | ✔️ | ✔️
xlsx-to-pdf | | ✔️ | ✔️
compress-pdf | | | ✔️
extract-image-scans | | | ✔️
ocr-pdf | | | ✔️
pdf-to-pdfa | | | ✔️
remove-blanks | | | ✔️
Operation | Ultra-Lite | Full
-------------------------|------------|-----
add-page-numbers | ✔️ | ✔️
add-password | ✔️ | ✔️
add-image | ✔️ | ✔️
add-watermark | ✔️ | ✔️
adjust-contrast | ✔️ | ✔️
auto-split-pdf | ✔️ | ✔️
auto-redact | ✔️ | ✔️
auto-rename | ✔️ | ✔️
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 | | ✔️

View File

@@ -1,18 +1,20 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.3'
id 'org.springframework.boot' version '3.2.4'
id 'io.spring.dependency-management' version '1.1.3'
id 'org.springdoc.openapi-gradle-plugin' version '1.8.0'
id "io.swagger.swaggerhub" version "1.3.2"
id 'edu.sc.seis.launch4j' version '3.0.5'
id 'com.diffplug.spotless' version '6.25.0'
id 'com.github.jk1.dependency-license-report' version '2.5'
id 'com.github.jk1.dependency-license-report' version '2.6'
}
import com.github.jk1.license.render.*
group = 'stirling.software'
version = '0.22.3'
version = '0.24.5'
//17 is lowest but we support and recommend 21
sourceCompatibility = '17'
repositories {
@@ -54,8 +56,8 @@ launch4j {
headerType="console"
jarTask = tasks.bootJar
errTitle="Encountered error, Do you have Java 17?"
downloadUrl="https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe"
errTitle="Encountered error, Do you have Java 21?"
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
variables=["BROWSER_OPEN=true", "ENDPOINTS_GROUPS_TO_REMOVE=CLI"]
jreMinVersion="17"
@@ -64,8 +66,8 @@ launch4j {
messagesStartupError="An error occurred while starting Stirling-PDF"
//messagesJreNotFoundError="This application requires a Java Runtime Environment, Please download Java 17."
messagesJreVersionError="You are running the wrong version of Java, Please download Java 17."
messagesLauncherError="Java is corrupted. Please uninstall and then install Java 17."
messagesJreVersionError="You are running the wrong version of Java, Please download Java 21."
messagesLauncherError="Java is corrupted. Please uninstall and then install Java 21."
messagesInstanceAlreadyExists="Stirling-PDF is already running."
}
@@ -87,24 +89,25 @@ dependencies {
//security updates
implementation 'ch.qos.logback:logback-classic:1.5.3'
implementation 'ch.qos.logback:logback-core:1.5.3'
implementation 'org.springframework:spring-webmvc:6.1.4'
implementation 'org.springframework:spring-webmvc:6.1.5'
implementation("io.github.pixee:java-security-toolkit:1.1.2")
implementation("io.github.pixee:java-security-toolkit:1.1.3")
implementation 'org.yaml:snakeyaml:2.2'
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.3'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.3'
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.4'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.4'
if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') {
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.3'
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.4'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.3"
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.4"
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client:3.2.4'
//2.2.x requires rebuild of DB file.. need migration path
implementation "com.h2database:h2:2.1.214"
}
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.3'
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.4'
// Batik
implementation 'org.apache.xmlgraphics:batik-all:1.17'
@@ -137,30 +140,30 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation ('org.apache.pdfbox:pdfbox:3.0.1'){
implementation ('org.apache.pdfbox:pdfbox:3.0.2'){
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation ('org.apache.pdfbox:xmpbox:3.0.1'){
implementation ('org.apache.pdfbox:xmpbox:3.0.2'){
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.3'
implementation 'io.micrometer:micrometer-core:1.12.3'
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.4'
implementation 'io.micrometer:micrometer-core:1.12.4'
implementation group: 'com.google.zxing', name: 'core', version: '3.5.3'
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation 'org.commonmark:commonmark:0.21.0'
implementation 'org.commonmark:commonmark-ext-gfm-tables:0.21.0'
implementation 'org.commonmark:commonmark:0.22.0'
implementation 'org.commonmark:commonmark-ext-gfm-tables:0.22.0'
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
implementation 'com.fathzer:javaluator:3.0.3'
developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.3")
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.28'
developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.4")
compileOnly 'org.projectlombok:lombok:1.18.32'
annotationProcessor 'org.projectlombok:lombok:1.18.32'
}
tasks.withType(JavaCompile) {

View File

@@ -1,6 +1,7 @@
apiVersion: v2
appVersion: 0.22.3
description: locally hosted web application that allows you to perform various operations on PDF files
appVersion: 0.24.5
description: locally hosted web application that allows you to perform various operations
on PDF files
home: https://github.com/Stirling-Tools/Stirling-PDF
keywords:
- stirling-pdf

View File

@@ -1,31 +0,0 @@
version: '3.3'
services:
stirling-pdf:
container_name: Stirling-PDF-Lite-Security
image: frooodle/s-pdf:latest-lite
deploy:
resources:
limits:
memory: 2G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- 8080:8080
volumes:
- /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw
environment:
DOCKER_ENABLE_SECURITY: "true"
SECURITY_ENABLELOGIN: "true"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF-Lite
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
restart: on-failure:5

View File

@@ -1,30 +0,0 @@
version: '3.3'
services:
stirling-pdf:
container_name: Stirling-PDF-Lite
image: frooodle/s-pdf:latest-lite
deploy:
resources:
limits:
memory: 2G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -qv 'Please sign in'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- 8080:8080
volumes:
- /stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw
environment:
DOCKER_ENABLE_SECURITY: "false"
SECURITY_ENABLELOGIN: "false"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF-Lite
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
restart: on-failure:5

View File

@@ -0,0 +1,40 @@
version: '3.3'
services:
stirling-pdf:
container_name: Stirling-PDF-Security
image: frooodle/s-pdf:latest
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- 8080:8080
volumes:
- /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw
environment:
DOCKER_ENABLE_SECURITY: "true"
SECURITY_ENABLELOGIN: "true"
SECURITY_OAUTH2_ENABLED: "true"
SECURITY_OAUTH2_AUTOCREATEUSER: "true" # This is set to true to allow auto-creation of non-existing users in Stirling-PDF
SECURITY_OAUTH2_ISSUER: "https://accounts.google.com" # Change with any other provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point
SECURITY_OAUTH2_CLIENTID: "<YOUR CLIENT ID>.apps.googleusercontent.com" # Client ID from your provider
SECURITY_OAUTH2_CLIENTSECRET: "<YOUR CLIENT SECRET>" # Client Secret from your provider
SECURITY_OAUTH2_SCOPES: "openid,profile,email" # Expected OAuth2 Scope
PUID: 1002
PGID: 1002
UMASK: "022"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security
UI_APPNAMENAVBAR: Stirling-PDF Latest
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
restart: on-failure:5

View File

@@ -21,6 +21,8 @@ services:
environment:
DOCKER_ENABLE_SECURITY: "false"
SECURITY_ENABLELOGIN: "false"
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
INSTALL_BOOK_AND_ADVANCED_HTML_OPS: "true"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest

BIN
images/stirling-home.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

View File

@@ -0,0 +1,192 @@
"""A script to update language progress status in README.md based on
properties file comparison.
This script compares default properties file with others in a directory to
determine language progress.
It then updates README.md based on provided progress list.
Author: Ludy87
Example:
To use this script, simply run it from command line:
$ python counter_translation.py
""" # noqa: D205
import glob
import os
import re
import tomlkit
import tomlkit.toml_file
def convert_to_multiline(data: tomlkit.TOMLDocument) -> tomlkit.TOMLDocument:
"""Converts 'ignore' and 'missing' arrays to multiline arrays and sorts the first-level keys of the TOML document.
Enhances readability and consistency in the TOML file by ensuring arrays contain unique and sorted entries.
Parameters:
data (tomlkit.TOMLDocument): The original TOML document containing the data.
Returns:
tomlkit.TOMLDocument: A new TOML document with sorted keys and properly formatted arrays.
""" # noqa: D205
sorted_data = tomlkit.document()
for key in sorted(data.keys()):
value = data[key]
if isinstance(value, dict):
new_table = tomlkit.table()
for subkey in ("ignore", "missing"):
if subkey in value:
# Convert the list to a set to remove duplicates, sort it, and convert to multiline for readability
unique_sorted_array = sorted(set(value[subkey]))
array = tomlkit.array()
array.multiline(True)
for item in unique_sorted_array:
array.append(item)
new_table[subkey] = array
sorted_data[key] = new_table
else:
# Add other types of data unchanged
sorted_data[key] = value
return sorted_data
def write_readme(progress_list: list[tuple[str, int]]) -> None:
"""Updates the progress status in the README.md file based
on the provided progress list.
Parameters:
progress_list (list[tuple[str, int]]): A list of tuples containing
language and progress percentage.
Returns:
None
""" # noqa: D205
with open("README.md", encoding="utf-8") as file:
content = file.readlines()
for i, line in enumerate(content[2:], start=2):
for progress in progress_list:
language, value = progress
if language in line:
if match := re.search(r"\!\[(\d+(\.\d+)?)%\]\(.*\)", line):
content[i] = line.replace(
match.group(0),
f"![{value}%](https://geps.dev/progress/{value})",
)
with open("README.md", "w", encoding="utf-8") as file:
file.writelines(content)
def compare_files(default_file_path, file_paths, translation_status_file) -> list[tuple[str, int]]:
"""Compares the default properties file with other
properties files in the directory.
Parameters:
default_file_path (str): The path to the default properties file.
files_directory (str): The directory containing other properties files.
Returns:
list[tuple[str, int]]: A list of tuples containing
language and progress percentage.
""" # noqa: D205
num_lines = sum(
1 for line in open(default_file_path, encoding="utf-8") if line.strip() and not line.strip().startswith("#")
)
result_list = []
sort_translation_status: tomlkit.TOMLDocument
# read toml
with open(translation_status_file, encoding="utf-8") as f:
sort_translation_status = tomlkit.parse(f.read())
for file_path in file_paths:
language = os.path.basename(file_path).split("messages_", 1)[1].split(".properties", 1)[0]
fails = 0
if "en_GB" in language or "en_US" in language:
result_list.append(("en_GB", 100))
result_list.append(("en_US", 100))
continue
if language not in sort_translation_status:
sort_translation_status[language] = tomlkit.table()
if (
"ignore" not in sort_translation_status[language]
or len(sort_translation_status[language].get("ignore", [])) < 1
):
sort_translation_status[language]["ignore"] = tomlkit.array(["language.direction"])
# if "missing" not in sort_translation_status[language]:
# sort_translation_status[language]["missing"] = tomlkit.array()
# elif "language.direction" in sort_translation_status[language]["missing"]:
# sort_translation_status[language]["missing"].remove("language.direction")
with open(default_file_path, encoding="utf-8") as default_file, open(file_path, encoding="utf-8") as file:
for _ in range(5):
next(default_file)
try:
next(file)
except StopIteration:
fails = num_lines
for line_num, (line_default, line_file) in enumerate(zip(default_file, file), start=6):
try:
# Ignoring empty lines and lines start with #
if line_default.strip() == "" or line_default.startswith("#"):
continue
default_key, default_value = line_default.split("=", 1)
file_key, file_value = line_file.split("=", 1)
if (
default_value.strip() == file_value.strip()
and default_key.strip() not in sort_translation_status[language]["ignore"]
):
print(f"{language}: Line {line_num} is missing the translation.")
# if default_key.strip() not in sort_translation_status[language]["missing"]:
# missing_array = tomlkit.array()
# missing_array.append(default_key.strip())
# missing_array.multiline(True)
# sort_translation_status[language]["missing"].extend(missing_array)
fails += 1
# elif default_key.strip() in sort_translation_status[language]["ignore"]:
# if default_key.strip() in sort_translation_status[language]["missing"]:
# sort_translation_status[language]["missing"].remove(default_key.strip())
if default_value.strip() != file_value.strip():
# if default_key.strip() in sort_translation_status[language]["missing"]:
# sort_translation_status[language]["missing"].remove(default_key.strip())
if default_key.strip() in sort_translation_status[language]["ignore"]:
sort_translation_status[language]["ignore"].remove(default_key.strip())
except IndexError:
pass
print(f"{language}: {fails} out of {num_lines} lines are not translated.")
result_list.append(
(
language,
int((num_lines - fails) * 100 / num_lines),
)
)
translation_status = convert_to_multiline(sort_translation_status)
with open(translation_status_file, "w", encoding="utf-8") as file:
file.write(tomlkit.dumps(translation_status))
unique_data = list(set(result_list))
unique_data.sort(key=lambda x: x[1], reverse=True)
return unique_data
if __name__ == "__main__":
directory = os.path.join(os.getcwd(), "src", "main", "resources")
messages_file_paths = glob.glob(os.path.join(directory, "messages_*.properties"))
reference_file = os.path.join(directory, "messages_en_GB.properties")
scripts_directory = os.path.join(os.getcwd(), "scripts")
translation_state_file = os.path.join(scripts_directory, "translation_status.toml")
write_readme(compare_files(reference_file, messages_file_paths, translation_state_file))

View File

@@ -1,26 +1,31 @@
#!/bin/sh
#!/bin/bash
# Update the user and group IDs as per environment variables
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
usermod -o -u "$PUID" stirlingpdfuser || true
fi
if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -f3)" ]; then
groupmod -o -g "$PGID" stirlingpdfgroup || true
fi
umask "$UMASK" || true
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then
apk add --no-cache calibre@testing
fi
/scripts/download-security-jar.sh
if [[ -n "$LANGS" ]]; then
/scripts/installFonts.sh $LANGS
fi
echo "Setting permissions and ownership for necessary directories..."
if chown -R stirlingpdfuser:stirlingpdfgroup $HOME /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar; then
chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar || true
# If chown succeeds, execute the command as stirlingpdfuser
# Attempt to change ownership of directories and files
if chown -R stirlingpdfuser:stirlingpdfgroup $HOME /logs /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /app.jar; then
chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /app.jar || true
# If chown succeeds, execute the command as stirlingpdfuser
exec su-exec stirlingpdfuser "$@"
else
# If chown fails, execute the command without changing the user context

View File

@@ -13,18 +13,6 @@ if [ -d /usr/share/tesseract-ocr/5/tessdata ]; then
cp -r /usr/share/tesseract-ocr/5/tessdata/* /usr/share/tessdata || true;
fi
# Update the user and group IDs as per environment variables
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
usermod -o -u "$PUID" stirlingpdfuser || true
fi
if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -f3)" ]; then
groupmod -o -g "$PGID" stirlingpdfgroup || true
fi
umask "$UMASK" || true
# Check if TESSERACT_LANGS environment variable is set and is not empty
if [[ -n "$TESSERACT_LANGS" ]]; then
# Convert comma-separated values to a space-separated list
@@ -40,20 +28,4 @@ if [[ -n "$TESSERACT_LANGS" ]]; then
done
fi
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then
apk add --no-cache calibre@testing
fi
/scripts/download-security-jar.sh
echo "Setting permissions and ownership for necessary directories..."
# Attempt to change ownership of directories and files
if chown -R stirlingpdfuser:stirlingpdfgroup $HOME /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar; then
chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar || true
# If chown succeeds, execute the command as stirlingpdfuser
exec su-exec stirlingpdfuser "$@"
else
# If chown fails, execute the command without changing the user context
echo "[WARN] Chown failed, running as host user"
exec "$@"
fi
/scripts/init-without-ocr.sh "$@"

67
scripts/installFonts.sh Normal file
View File

@@ -0,0 +1,67 @@
#!/bin/bash
LANGS=$1
# Function to install a font package
install_font() {
echo "Installing font package: $1"
if ! apk add "$1" --no-cache; then
echo "Failed to install $1"
fi
}
# Install common fonts used across many languages
#common_fonts=(
# font-terminus
# font-dejavu
# font-noto
# font-noto-cjk
# font-awesome
# font-noto-extra
#)
#
#for font in "${common_fonts[@]}"; do
# install_font $font
#done
# Map languages to specific font packages
declare -A language_fonts=(
["ar_AR"]="font-noto-arabic"
["zh_CN"]="font-isas-misc"
["zh_TW"]="font-isas-misc"
["ja_JP"]="font-noto font-noto-thai font-noto-tibetan font-ipa font-sony-misc font-jis-misc"
["ru_RU"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic"
["sr_LATN_RS"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic"
["uk_UA"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic"
["ko_KR"]="font-noto font-noto-thai font-noto-tibetan"
["el_GR"]="font-noto"
["hi_IN"]="font-noto-devanagari"
["bg_BG"]="font-vollkorn font-misc-cyrillic"
["GENERAL"]="font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra"
)
# Install fonts for other languages which generally do not need special packages beyond 'font-noto'
other_langs=("en_GB" "en_US" "de_DE" "fr_FR" "es_ES" "ca_CA" "it_IT" "pt_BR" "nl_NL" "sv_SE" "pl_PL" "ro_RO" "hu_HU" "tr_TR" "id_ID" "eu_ES")
if [[ $LANGS == "ALL" ]]; then
# Install all fonts from the language_fonts map
for fonts in "${language_fonts[@]}"; do
for font in $fonts; do
install_font $font
done
done
else
# Split comma-separated languages and install necessary fonts
IFS=',' read -ra LANG_CODES <<< "$LANGS"
for code in "${LANG_CODES[@]}"; do
if [[ " ${other_langs[@]} " =~ " ${code} " ]]; then
install_font font-noto
else
fonts_to_install=${language_fonts[$code]}
if [ ! -z "$fonts_to_install" ]; then
for font in $fonts_to_install; do
install_font $font
done
fi
fi
done
fi

View File

@@ -0,0 +1,159 @@
[ar_AR]
ignore = [
'language.direction',
]
[bg_BG]
ignore = [
'language.direction',
]
[ca_CA]
ignore = [
'language.direction',
]
[de_DE]
ignore = [
'AddStampRequest.alphabet',
'AddStampRequest.position',
'PDFToBook.selectText.1',
'PDFToText.tags',
'addPageNumbers.selectText.3',
'alphabet',
'certSign.name',
'language.direction',
'licenses.version',
'pipeline.title',
'pipelineOptions.pipelineHeader',
'sponsor',
'text',
'watermark.type.1',
]
[el_GR]
ignore = [
'language.direction',
]
[es_ES]
ignore = [
'adminUserSettings.roles',
'color',
'language.direction',
'no',
'showJS.tags',
]
[eu_ES]
ignore = [
'language.direction',
]
[fr_FR]
ignore = [
'language.direction',
]
[hi_IN]
ignore = [
'language.direction',
]
[hu_HU]
ignore = [
'language.direction',
]
[id_ID]
ignore = [
'language.direction',
]
[it_IT]
ignore = [
'font',
'language.direction',
'no',
'password',
'pipeline.title',
'pipelineOptions.pipelineHeader',
'removePassword.selectText.2',
'showJS.tags',
'sponsor',
]
[ja_JP]
ignore = [
'language.direction',
]
[ko_KR]
ignore = [
'language.direction',
]
[nl_NL]
ignore = [
'language.direction',
]
[pl_PL]
ignore = [
'language.direction',
]
[pt_BR]
ignore = [
'language.direction',
]
[pt_PT]
ignore = [
'language.direction',
]
[ro_RO]
ignore = [
'language.direction',
]
[ru_RU]
ignore = [
'language.direction',
]
[sk_SK]
ignore = [
'language.direction',
]
[sr_LATN_RS]
ignore = [
'language.direction',
]
[sv_SE]
ignore = [
'language.direction',
]
[tr_TR]
ignore = [
'language.direction',
]
[uk_UA]
ignore = [
'language.direction',
]
[zh_CN]
ignore = [
'language.direction',
]
[zh_TW]
ignore = [
'language.direction',
]

View File

@@ -5,6 +5,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,16 +64,39 @@ public class SPdfApplication {
}
public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication app = new SpringApplication(SPdfApplication.class);
app.addInitializers(new ConfigInitializer());
Map<String, String> propertyFiles = new HashMap<>();
// stirling pdf settings file
if (Files.exists(Paths.get("configs/settings.yml"))) {
app.setDefaultProperties(
Collections.singletonMap(
"spring.config.additional-location", "file:configs/settings.yml"));
propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml");
} else {
logger.warn(
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
}
// custom javs settings file
if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
String existing = propertyFiles.getOrDefault("spring.config.additional-location", "");
if (!existing.isEmpty()) {
existing += ",";
}
propertyFiles.put(
"spring.config.additional-location",
existing + "file:configs/custom_settings.yml");
} else {
logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
}
if (!propertyFiles.isEmpty()) {
app.setDefaultProperties(
Collections.singletonMap(
"spring.config.additional-location",
propertyFiles.get("spring.config.additional-location")));
}
app.run(args);
try {

View File

@@ -6,18 +6,35 @@ import java.nio.file.Paths;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.spring6.SpringTemplateEngine;
import stirling.software.SPDF.model.ApplicationProperties;
@Configuration
@Lazy
public class AppConfig {
@Autowired ApplicationProperties applicationProperties;
@Bean
@ConditionalOnProperty(
name = "system.customHTMLFiles",
havingValue = "true",
matchIfMissing = false)
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
return templateEngine;
}
@Bean(name = "loginEnabled")
public boolean loginEnabled() {
return applicationProperties.getSecurity().getEnableLogin();
@@ -85,4 +102,10 @@ public class AppConfig {
}
return "true".equalsIgnoreCase(installOps);
}
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
@Bean(name = "activSecurity")
public boolean missingActivSecurity() {
return false;
}
}

View File

@@ -0,0 +1,25 @@
package stirling.software.SPDF.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import stirling.software.SPDF.model.ApplicationProperties;
@Service
class AppUpdateService {
@Autowired private ApplicationProperties applicationProperties;
@Autowired(required = false)
ShowAdminInterface showAdmin;
@Bean(name = "shouldShow")
@Scope("request")
public boolean shouldShow() {
boolean showUpdate = applicationProperties.getSystem().getShowUpdate();
boolean showAdminResult = (showAdmin != null) ? showAdmin.getShowUpdateOnlyAdmins() : true;
return showUpdate && showAdminResult;
}
}

View File

@@ -15,7 +15,14 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
private static final List<String> ALLOWED_PARAMS =
Arrays.asList(
"lang", "endpoint", "endpoints", "logout", "error", "file", "messageType");
"lang",
"endpoint",
"endpoints",
"logout",
"error",
"erroroauth",
"file",
"messageType");
@Override
public boolean preHandle(

View File

@@ -1,20 +1,18 @@
package stirling.software.SPDF.config;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@@ -26,12 +24,12 @@ public class ConfigInitializer
public void initialize(ConfigurableApplicationContext applicationContext) {
try {
ensureConfigExists();
} catch (IOException e) {
} catch (Exception e) {
throw new RuntimeException("Failed to initialize application configuration", e);
}
}
public void ensureConfigExists() throws IOException {
public void ensureConfigExists() throws IOException, URISyntaxException {
// Define the path to the external config directory
Path destPath = Paths.get("configs", "settings.yml");
@@ -51,170 +49,88 @@ public class ConfigInitializer
}
}
} else {
// If user file exists, we need to merge it with the template from the classpath
List<String> templateLines;
try (InputStream in =
getClass().getClassLoader().getResourceAsStream("settings.yml.template")) {
templateLines =
new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
.lines()
.collect(Collectors.toList());
}
Path templatePath =
Paths.get(
getClass()
.getClassLoader()
.getResource("settings.yml.template")
.toURI());
Path userPath = Paths.get("configs", "settings.yml");
mergeYamlFiles(templateLines, destPath, destPath);
}
}
List<String> templateLines = Files.readAllLines(templatePath);
List<String> userLines =
Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>();
public void mergeYamlFiles(List<String> templateLines, Path userFilePath, Path outputPath)
throws IOException {
List<String> userLines = Files.readAllLines(userFilePath);
List<String> mergedLines = new ArrayList<>();
boolean insideAutoGenerated = false;
boolean beforeFirstKey = true;
List<String> resultLines = new ArrayList<>();
Function<String, Boolean> isCommented = line -> line.trim().startsWith("#");
Function<String, String> extractKey =
line -> {
String[] parts = line.split(":");
return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : "";
};
Function<String, Integer> getIndentationLevel =
line -> {
int count = 0;
for (char ch : line.toCharArray()) {
if (ch == ' ') count++;
else break;
for (String templateLine : templateLines) {
// Check if the line is a comment
if (templateLine.trim().startsWith("#")) {
String entry = templateLine.trim().substring(1).trim();
if (!entry.isEmpty()) {
// Check if this comment has been uncommented in userLines
String key = entry.split(":")[0].trim();
addLine(resultLines, userLines, templateLine, key);
} else {
resultLines.add(templateLine);
}
return count;
};
Set<String> userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet());
for (String line : templateLines) {
String key = extractKey.apply(line);
if ("AutomaticallyGenerated:".equalsIgnoreCase(line.trim())) {
insideAutoGenerated = true;
mergedLines.add(line);
continue;
} else if (insideAutoGenerated && line.trim().isEmpty()) {
insideAutoGenerated = false;
mergedLines.add(line);
continue;
}
if (beforeFirstKey && (isCommented.apply(line) || line.trim().isEmpty())) {
// Handle top comments and empty lines before the first key.
mergedLines.add(line);
continue;
}
if (!key.isEmpty()) beforeFirstKey = false;
if (userKeys.contains(key)) {
// If user has any version (commented or uncommented) of this key, skip the
// template line
Optional<String> userValue =
userLines.stream()
.filter(
l ->
extractKey.apply(l).equalsIgnoreCase(key)
&& !isCommented.apply(l))
.findFirst();
if (userValue.isPresent()) mergedLines.add(userValue.get());
continue;
}
if (isCommented.apply(line) || line.trim().isEmpty() || !userKeys.contains(key)) {
mergedLines.add(
line); // If line is commented, empty or key not present in user's file,
// retain the
// template line
continue;
}
}
// Add any additional uncommented user lines that are not present in the
// template
for (String userLine : userLines) {
String userKey = extractKey.apply(userLine);
boolean isPresentInTemplate =
templateLines.stream()
.map(extractKey)
.anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey));
if (!isPresentInTemplate && !isCommented.apply(userLine)) {
if (!childOfTemplateEntry(
isCommented,
extractKey,
getIndentationLevel,
userLines,
userLine,
templateLines)) {
// check if userLine is a child of a entry within templateLines or not, if child
// of parent in templateLines then dont add to mergedLines, if anything else
// then add
mergedLines.add(userLine);
}
// Check if the line is a key-value pair
else if (templateLine.contains(":")) {
String key = templateLine.split(":")[0].trim();
addLine(resultLines, userLines, templateLine, key);
}
// Handle empty lines
else if (templateLine.trim().length() == 0) {
resultLines.add("");
}
}
// Write the result to the user settings file
Files.write(userPath, resultLines);
}
Files.write(outputPath, mergedLines, StandardCharsets.UTF_8);
Path customSettingsPath = Paths.get("configs", "custom_settings.yml");
if (!Files.exists(customSettingsPath)) {
Files.createFile(customSettingsPath);
}
}
// New method to check if a userLine is a child of an entry in templateLines
boolean childOfTemplateEntry(
Function<String, Boolean> isCommented,
Function<String, String> extractKey,
Function<String, Integer> getIndentationLevel,
List<String> userLines,
String userLine,
List<String> templateLines) {
String userKey = extractKey.apply(userLine).trim();
int userIndentation = getIndentationLevel.apply(userLine);
// Start by assuming the line is not a child of an entry in templateLines
boolean isChild = false;
// Iterate backwards through userLines from the current line to find any parent
for (int i = userLines.indexOf(userLine) - 1; i >= 0; i--) {
String potentialParentLine = userLines.get(i);
int parentIndentation = getIndentationLevel.apply(potentialParentLine);
// Check if we've reached a potential parent based on indentation
if (parentIndentation < userIndentation) {
String parentKey = extractKey.apply(potentialParentLine).trim();
// Now, check if this potential parent or any of its parents exist in templateLines
boolean parentExistsInTemplate =
templateLines.stream()
.filter(line -> !isCommented.apply(line)) // Skip commented lines
.anyMatch(
templateLine -> {
String templateKey =
extractKey.apply(templateLine).trim();
return parentKey.equalsIgnoreCase(templateKey);
});
if (!parentExistsInTemplate) {
// If the parent does not exist in template, check the next level parent
userIndentation =
parentIndentation; // Update userIndentation to the parent's indentation
// for next iteration
if (parentIndentation == 0) {
// If we've reached the top-level parent and it's not in template, the
// original line is considered not a child
isChild = false;
break;
}
} else {
// If any parent exists in template, the original line is considered a child
isChild = true;
//TODO check parent value instead of just indent lines for duplicate keys (like enabled etc)
private static void addLine(List<String> resultLines, List<String> userLines, String templateLine, String key) {
boolean added = false;
int templateIndentationLevel = getIndentationLevel(templateLine);
for (String settingsLine : userLines) {
if (settingsLine.trim().startsWith(key + ":")) {
int settingsIndentationLevel = getIndentationLevel(settingsLine);
// Check if it is correct settingsLine and has the same parent as templateLine
if (settingsIndentationLevel == templateIndentationLevel) {
resultLines.add(settingsLine);
added = true;
break;
}
}
}
if (!added) {
resultLines.add(templateLine);
}
}
return isChild; // Return true if the line is not a child of any entry in templateLines
private static int getIndentationLevel(String line) {
int indentationLevel = 0;
String trimmedLine = line.trim();
if (trimmedLine.startsWith("#")) {
line = trimmedLine.substring(1);
}
for (char c : line.toCharArray()) {
if (c == ' ') {
indentationLevel++;
} else {
break;
}
}
return indentationLevel;
}
}

View File

@@ -129,7 +129,7 @@ public class EndpointConfiguration {
addEndpointToGroup("Other", "sign");
addEndpointToGroup("Other", "flatten");
addEndpointToGroup("Other", "repair");
addEndpointToGroup("Other", "remove-blanks");
addEndpointToGroup("Other", REMOVE_BLANKS);
addEndpointToGroup("Other", "remove-annotations");
addEndpointToGroup("Other", "compare");
addEndpointToGroup("Other", "add-page-numbers");
@@ -146,7 +146,6 @@ public class EndpointConfiguration {
addEndpointToGroup("CLI", "xlsx-to-pdf");
addEndpointToGroup("CLI", "pdf-to-word");
addEndpointToGroup("CLI", "pdf-to-presentation");
addEndpointToGroup("CLI", "pdf-to-text");
addEndpointToGroup("CLI", "pdf-to-html");
addEndpointToGroup("CLI", "pdf-to-xml");
addEndpointToGroup("CLI", "ocr-pdf");
@@ -154,6 +153,7 @@ public class EndpointConfiguration {
addEndpointToGroup("CLI", "url-to-pdf");
addEndpointToGroup("CLI", "book-to-pdf");
addEndpointToGroup("CLI", "pdf-to-book");
addEndpointToGroup("CLI", "pdf-to-rtf");
// Calibre
addEndpointToGroup("Calibre", "book-to-pdf");
@@ -161,13 +161,13 @@ public class EndpointConfiguration {
// python
addEndpointToGroup("Python", "extract-image-scans");
addEndpointToGroup("Python", "remove-blanks");
addEndpointToGroup("Python", REMOVE_BLANKS);
addEndpointToGroup("Python", "html-to-pdf");
addEndpointToGroup("Python", "url-to-pdf");
// openCV
addEndpointToGroup("OpenCV", "extract-image-scans");
addEndpointToGroup("OpenCV", "remove-blanks");
addEndpointToGroup("OpenCV", REMOVE_BLANKS);
// LibreOffice
addEndpointToGroup("LibreOffice", "repair");
@@ -175,7 +175,7 @@ public class EndpointConfiguration {
addEndpointToGroup("LibreOffice", "xlsx-to-pdf");
addEndpointToGroup("LibreOffice", "pdf-to-word");
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
addEndpointToGroup("LibreOffice", "pdf-to-text");
addEndpointToGroup("LibreOffice", "pdf-to-rtf");
addEndpointToGroup("LibreOffice", "pdf-to-html");
addEndpointToGroup("LibreOffice", "pdf-to-xml");
@@ -217,7 +217,8 @@ public class EndpointConfiguration {
addEndpointToGroup("Java", "split-by-size-or-count");
addEndpointToGroup("Java", "overlay-pdf");
addEndpointToGroup("Java", "split-pdf-by-sections");
addEndpointToGroup("Java", "remove-blanks");
addEndpointToGroup("Java", REMOVE_BLANKS);
addEndpointToGroup("Java", "pdf-to-text");
// Javascript
addEndpointToGroup("Javascript", "pdf-organizer");
@@ -244,4 +245,6 @@ public class EndpointConfiguration {
}
}
}
private static final String REMOVE_BLANKS = "remove-blanks";
}

View File

@@ -0,0 +1,48 @@
package stirling.software.SPDF.config;
import java.io.IOException;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
import org.thymeleaf.templateresource.ClassLoaderTemplateResource;
import org.thymeleaf.templateresource.FileTemplateResource;
import org.thymeleaf.templateresource.ITemplateResource;
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
private final ResourceLoader resourceLoader;
public FileFallbackTemplateResolver(ResourceLoader resourceLoader) {
super();
this.resourceLoader = resourceLoader;
setSuffix(".html");
}
// Note this does not work in local IDE, Prod jar only.
@Override
protected ITemplateResource computeTemplateResource(
IEngineConfiguration configuration,
String ownerTemplate,
String template,
String resourceName,
String characterEncoding,
Map<String, Object> templateResolutionAttributes) {
Resource resource =
resourceLoader.getResource("file:./customFiles/templates/" + resourceName);
try {
if (resource.exists() && resource.isReadable()) {
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
}
} catch (IOException e) {
}
return new ClassLoaderTemplateResource(
Thread.currentThread().getContextClassLoader(),
"classpath:/templates/" + resourceName,
characterEncoding);
}
}

View File

@@ -0,0 +1,7 @@
package stirling.software.SPDF.config;
public interface ShowAdminInterface {
default boolean getShowUpdateOnlyAdmins() {
return true;
}
}

View File

@@ -0,0 +1,46 @@
package stirling.software.SPDF.config.security;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import stirling.software.SPDF.config.ShowAdminInterface;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.UserRepository;
@Service
class AppUpdateAuthService implements ShowAdminInterface {
@Autowired private UserRepository userRepository;
@Autowired private ApplicationProperties applicationProperties;
public boolean getShowUpdateOnlyAdmins() {
boolean showUpdate = applicationProperties.getSystem().getShowUpdate();
if (!showUpdate) {
return showUpdate;
}
boolean showUpdateOnlyAdmin = applicationProperties.getSystem().getShowUpdateOnlyAdmin();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return !showUpdateOnlyAdmin;
}
if (authentication.getName().equalsIgnoreCase("anonymousUser")) {
return !showUpdateOnlyAdmin;
}
Optional<User> user = userRepository.findByUsername(authentication.getName());
if (user.isPresent() && showUpdateOnlyAdmin) {
return "ROLE_ADMIN".equals(user.get().getRolesAsString());
}
return showUpdate;
}
}

View File

@@ -3,27 +3,31 @@ package stirling.software.SPDF.config.security;
import java.io.IOException;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.model.User;
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Autowired private final LoginAttemptService loginAttemptService;
private LoginAttemptService loginAttemptService;
@Autowired private final UserService userService; // Inject the UserService
private UserService userService;
private static final Logger logger =
LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
public CustomAuthenticationFailureHandler(
LoginAttemptService loginAttemptService, UserService userService) {
final LoginAttemptService loginAttemptService, UserService userService) {
this.loginAttemptService = loginAttemptService;
this.userService = userService;
}
@@ -34,29 +38,40 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
String ip = request.getRemoteAddr();
logger.error("Failed login attempt from IP: " + ip);
logger.error("Failed login attempt from IP: {}", ip);
if (exception.getClass().isAssignableFrom(InternalAuthenticationServiceException.class)
|| "Password must not be null".equalsIgnoreCase(exception.getMessage())) {
response.sendRedirect("/login?error=oauth2AuthenticationError");
return;
}
String username = request.getParameter("username");
if (!isDemoUser(username)) {
if (loginAttemptService.loginAttemptCheck(username)) {
setDefaultFailureUrl("/login?error=locked");
} else {
if (exception.getClass().isAssignableFrom(LockedException.class)) {
setDefaultFailureUrl("/login?error=locked");
}
if (username != null && !isDemoUser(username)) {
logger.info(
"Remaining attempts for user {}: {}",
username,
loginAttemptService.getRemainingAttempts(username));
loginAttemptService.loginFailed(username);
if (loginAttemptService.isBlocked(username)
|| exception.getClass().isAssignableFrom(LockedException.class)) {
response.sendRedirect("/login?error=locked");
return;
}
}
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
setDefaultFailureUrl("/login?error=badcredentials");
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)
|| exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) {
response.sendRedirect("/login?error=badcredentials");
return;
}
super.onAuthenticationFailure(request, response, exception);
}
private boolean isDemoUser(String username) {
Optional<User> user = userService.findByUsername(username);
Optional<User> user = userService.findByUsernameIgnoreCase(username);
return user.isPresent()
&& user.get().getAuthorities().stream()
.anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority()));

View File

@@ -2,11 +2,9 @@ package stirling.software.SPDF.config.security;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@@ -14,25 +12,30 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.utils.RequestUriUtils;
@Component
public class CustomAuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired private LoginAttemptService loginAttemptService;
private LoginAttemptService loginAttemptService;
public CustomAuthenticationSuccessHandler(LoginAttemptService loginAttemptService) {
this.loginAttemptService = loginAttemptService;
}
@Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
String username = request.getParameter("username");
loginAttemptService.loginSucceeded(username);
String userName = request.getParameter("username");
loginAttemptService.loginSucceeded(userName);
// Get the saved request
HttpSession session = request.getSession(false);
SavedRequest savedRequest =
session != null
(session != null)
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
: null;
if (savedRequest != null
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
// Redirect to the original destination

View File

@@ -0,0 +1,33 @@
package stirling.software.SPDF.config.security;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
@Autowired SessionRegistry sessionRegistry;
@Override
public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
HttpSession session = request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry.removeSessionInformation(sessionId);
session.invalidate();
logger.debug("Session invalidated: " + sessionId);
}
response.sendRedirect(request.getContextPath() + "/login?logout=true");
}
}

View File

@@ -39,6 +39,10 @@ public class CustomUserDetailsService implements UserDetailsService {
"Your account has been locked due to too many failed login attempts.");
}
if (!user.hasPassword()) {
throw new IllegalArgumentException("Password must not be null");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),

View File

@@ -39,7 +39,7 @@ public class FirstLoginFilter extends OncePerRequestFilter {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
Optional<User> user = userService.findByUsername(authentication.getName());
Optional<User> user = userService.findByUsernameIgnoreCase(authentication.getName());
if ("GET".equalsIgnoreCase(method)
&& user.isPresent()
&& user.get().isFirstLogin()

View File

@@ -7,6 +7,8 @@ import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -19,43 +21,70 @@ public class InitialSecuritySetup {
@Autowired private UserService userService;
@Autowired ApplicationProperties applicationProperties;
@Autowired private ApplicationProperties applicationProperties;
private static final Logger logger = LoggerFactory.getLogger(InitialSecuritySetup.class);
@PostConstruct
public void init() {
if (!userService.hasUsers()) {
String initialUsername =
applicationProperties.getSecurity().getInitialLogin().getUsername();
String initialPassword =
applicationProperties.getSecurity().getInitialLogin().getPassword();
if (initialUsername != null && initialPassword != null) {
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
} else {
initialUsername = "admin";
initialPassword = "stirling";
userService.saveUser(
initialUsername, initialPassword, Role.ADMIN.getRoleId(), true);
}
}
if (!userService.usernameExists(Role.INTERNAL_API_USER.getRoleId())) {
userService.saveUser(
Role.INTERNAL_API_USER.getRoleId(),
UUID.randomUUID().toString(),
Role.INTERNAL_API_USER.getRoleId());
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
initializeAdminUser();
}
initializeInternalApiUser();
}
@PostConstruct
public void initSecretKey() throws IOException {
String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
if (secretKey == null || secretKey.isEmpty()) {
if (!isValidUUID(secretKey)) {
secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
saveKeyToConfig(secretKey);
}
}
private void initializeAdminUser() {
String initialUsername =
applicationProperties.getSecurity().getInitialLogin().getUsername();
String initialPassword =
applicationProperties.getSecurity().getInitialLogin().getPassword();
if (initialUsername != null
&& !initialUsername.isEmpty()
&& initialPassword != null
&& !initialPassword.isEmpty()
&& !userService.findByUsernameIgnoreCase(initialUsername).isPresent()) {
try {
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
logger.info("Admin user created: " + initialUsername);
} catch (IllegalArgumentException e) {
logger.error("Failed to initialize security setup", e);
System.exit(1);
}
} else {
createDefaultAdminUser();
}
}
private void createDefaultAdminUser() {
String defaultUsername = "admin";
String defaultPassword = "stirling";
if (!userService.findByUsernameIgnoreCase(defaultUsername).isPresent()) {
userService.saveUser(defaultUsername, defaultPassword, Role.ADMIN.getRoleId(), true);
logger.info("Default admin user created: " + defaultUsername);
}
}
private void initializeInternalApiUser() {
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
userService.saveUser(
Role.INTERNAL_API_USER.getRoleId(),
UUID.randomUUID().toString(),
Role.INTERNAL_API_USER.getRoleId());
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
logger.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
}
}
private void saveKeyToConfig(String key) throws IOException {
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
List<String> lines = Files.readAllLines(path);
@@ -85,4 +114,16 @@ public class InitialSecuritySetup {
// Write back to the file
Files.write(path, lines);
}
private boolean isValidUUID(String uuid) {
if (uuid == null) {
return false;
}
try {
UUID.fromString(uuid);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
}

View File

@@ -3,6 +3,8 @@ package stirling.software.SPDF.config.security;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -15,44 +17,62 @@ public class LoginAttemptService {
@Autowired ApplicationProperties applicationProperties;
private int MAX_ATTEMPTS;
private static final Logger logger = LoggerFactory.getLogger(LoginAttemptService.class);
private int MAX_ATTEMPT;
private long ATTEMPT_INCREMENT_TIME;
private ConcurrentHashMap<String, AttemptCounter> attemptsCache;
@PostConstruct
public void init() {
MAX_ATTEMPTS = applicationProperties.getSecurity().getLoginAttemptCount();
MAX_ATTEMPT = applicationProperties.getSecurity().getLoginAttemptCount();
ATTEMPT_INCREMENT_TIME =
TimeUnit.MINUTES.toMillis(
applicationProperties.getSecurity().getLoginResetTimeMinutes());
attemptsCache = new ConcurrentHashMap<>();
}
private final ConcurrentHashMap<String, AttemptCounter> attemptsCache =
new ConcurrentHashMap<>();
public void loginSucceeded(String key) {
attemptsCache.remove(key);
logger.info(key + " " + attemptsCache.mappingCount());
if (key == null || key.trim().isEmpty()) {
return;
}
attemptsCache.remove(key.toLowerCase());
}
public boolean loginAttemptCheck(String key) {
attemptsCache.compute(
key,
(k, attemptCounter) -> {
if (attemptCounter == null
|| attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
return new AttemptCounter();
} else {
attemptCounter.increment();
return attemptCounter;
}
});
return attemptsCache.get(key).getAttemptCount() >= MAX_ATTEMPTS;
public void loginFailed(String key) {
if (key == null || key.trim().isEmpty()) return;
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
if (attemptCounter == null) {
attemptCounter = new AttemptCounter();
attemptsCache.put(key.toLowerCase(), attemptCounter);
} else {
if (attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
attemptCounter.reset();
}
attemptCounter.increment();
}
}
public boolean isBlocked(String key) {
AttemptCounter attemptCounter = attemptsCache.get(key);
if (attemptCounter != null) {
return attemptCounter.getAttemptCount() >= MAX_ATTEMPTS;
if (key == null || key.trim().isEmpty()) return false;
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
if (attemptCounter == null) {
return false;
}
return false;
return attemptCounter.getAttemptCount() >= MAX_ATTEMPT;
}
public int getRemainingAttempts(String key) {
if (key == null || key.trim().isEmpty()) return MAX_ATTEMPT;
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
if (attemptCounter == null) {
return MAX_ATTEMPT;
}
return MAX_ATTEMPT - attemptCounter.getAttemptCount();
}
}

View File

@@ -1,7 +1,10 @@
package stirling.software.SPDF.config.security;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@@ -10,18 +13,31 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@Configuration
@@ -29,7 +45,7 @@ import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@EnableMethodSecurity
public class SecurityConfiguration {
@Autowired private UserDetailsService userDetailsService;
@Autowired private CustomUserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
@@ -42,6 +58,8 @@ public class SecurityConfiguration {
@Qualifier("loginEnabled")
public boolean loginEnabledValue;
@Autowired ApplicationProperties applicationProperties;
@Autowired private UserAuthenticationFilter userAuthenticationFilter;
@Autowired private LoginAttemptService loginAttemptService;
@@ -76,7 +94,8 @@ public class SecurityConfiguration {
formLogin
.loginPage("/login")
.successHandler(
new CustomAuthenticationSuccessHandler())
new CustomAuthenticationSuccessHandler(
loginAttemptService))
.defaultSuccessUrl("/")
.failureHandler(
new CustomAuthenticationFailureHandler(
@@ -87,20 +106,9 @@ public class SecurityConfiguration {
logout ->
logout.logoutRequestMatcher(
new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout=true")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.invalidateHttpSession(true) // Invalidate session
.deleteCookies("JSESSIONID", "remember-me")
.addLogoutHandler(
(request, response, authentication) -> {
HttpSession session =
request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry()
.removeSessionInformation(
sessionId);
}
}))
.deleteCookies("JSESSIONID", "remember-me"))
.rememberMe(
rememberMeConfigurer ->
rememberMeConfigurer // Use the configurator directly
@@ -124,6 +132,7 @@ public class SecurityConfiguration {
: uri;
return trimmedUri.startsWith("/login")
|| trimmedUri.startsWith("/oauth")
|| trimmedUri.endsWith(".svg")
|| trimmedUri.startsWith(
"/register")
@@ -138,8 +147,44 @@ public class SecurityConfiguration {
.permitAll()
.anyRequest()
.authenticated())
.userDetailsService(userDetailsService)
.authenticationProvider(authenticationProvider());
// Handle OAUTH2 Logins
if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
http.oauth2Login(
oauth2 ->
oauth2.loginPage("/oauth2")
/*
This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database.
If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser'
is set as true, else login fails with an error message advising the same.
*/
.successHandler(
new CustomOAuth2AuthenticationSuccessHandler(
loginAttemptService,
applicationProperties,
userService))
.failureHandler(
new CustomOAuth2AuthenticationFailureHandler())
// Add existing Authorities from the database
.userInfoEndpoint(
userInfoEndpoint ->
userInfoEndpoint
.oidcUserService(
new CustomOAuth2UserService(
applicationProperties,
userService,
loginAttemptService))
.userAuthoritiesMapper(
userAuthoritiesMapper())))
.logout(
logout ->
logout.logoutSuccessHandler(
new CustomOAuth2LogoutSuccessHandler(
this.applicationProperties,
sessionRegistry())));
}
} else {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
@@ -148,6 +193,70 @@ public class SecurityConfiguration {
return http.build();
}
// Client Registration Repository for OAUTH2 OIDC Login
@Bean
@ConditionalOnProperty(
value = "security.oauth2.enabled",
havingValue = "true",
matchIfMissing = false)
public ClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryClientRegistrationRepository(this.oidcClientRegistration());
}
private ClientRegistration oidcClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
return ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
.registrationId("oidc")
.clientId(oauth.getClientId())
.clientSecret(oauth.getClientSecret())
.scope(oauth.getScopes())
.userNameAttributeName(oauth.getUseAsUsername())
.clientName("OIDC")
.build();
}
/*
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
This is required for the internal; 'hasRole()' function to give out the correct role.
*/
@Bean
@ConditionalOnProperty(
value = "security.oauth2.enabled",
havingValue = "true",
matchIfMissing = false)
GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
authorities.forEach(
authority -> {
// Add existing OAUTH2 Authorities
mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
// Add Authorities from database for existing user, if user is present.
if (authority instanceof OAuth2UserAuthority oauth2Auth) {
String useAsUsername =
applicationProperties
.getSecurity()
.getOAUTH2()
.getUseAsUsername();
Optional<User> userOpt =
userService.findByUsernameIgnoreCase(
(String) oauth2Auth.getAttributes().get(useAsUsername));
if (userOpt.isPresent()) {
User user = userOpt.get();
if (user != null) {
mappedAuthorities.add(
new SimpleGrantedAuthority(
userService.findRole(user).getAuthority()));
}
}
}
});
return mappedAuthorities;
};
}
@Bean
public IPRateLimitingFilter rateLimitingFilter() {
int maxRequestsPerIp = 1000000; // Example limit TODO add config level
@@ -166,4 +275,9 @@ public class SecurityConfiguration {
public PersistentTokenRepository persistentTokenRepository() {
return new JPATokenRepositoryImpl();
}
@Bean
public boolean activSecurity() {
return true;
}
}

View File

@@ -20,6 +20,7 @@ import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.ConsumptionProbe;
import io.github.bucket4j.Refill;
import io.github.pixee.security.Newlines;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
@@ -125,12 +126,16 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
ConsumptionProbe probe = userBucket.tryConsumeAndReturnRemaining(1);
if (probe.isConsumed()) {
response.setHeader("X-Rate-Limit-Remaining", Long.toString(probe.getRemainingTokens()));
response.setHeader(
"X-Rate-Limit-Remaining",
Newlines.stripAll(Long.toString(probe.getRemainingTokens())));
filterChain.doFilter(request, response);
} else {
long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000;
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.setHeader("X-Rate-Limit-Retry-After-Seconds", String.valueOf(waitForRefill));
response.setHeader(
"X-Rate-Limit-Retry-After-Seconds",
Newlines.stripAll(String.valueOf(waitForRefill)));
response.getWriter().write("Rate limit exceeded for POST requests.");
}
}

View File

@@ -8,6 +8,8 @@ import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -18,9 +20,11 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.Authority;
import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.AuthorityRepository;
import stirling.software.SPDF.repository.UserRepository;
@Service
@@ -28,8 +32,28 @@ public class UserService implements UserServiceInterface {
@Autowired private UserRepository userRepository;
@Autowired private AuthorityRepository authorityRepository;
@Autowired private PasswordEncoder passwordEncoder;
@Autowired private MessageSource messageSource;
// Handle OAUTH2 login and user auto creation.
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) {
if (!isUsernameValid(username)) {
return false;
}
Optional<User> existingUser = userRepository.findByUsernameIgnoreCase(username);
if (existingUser.isPresent()) {
return true;
}
if (autoCreateUser) {
saveUser(username, AuthenticationType.OAUTH2);
return true;
}
return false;
}
public Authentication getAuthentication(String apiKey) {
User user = getUserByApiKey(apiKey);
if (user == null) {
@@ -62,7 +86,7 @@ public class UserService implements UserServiceInterface {
public User addApiKeyToUser(String username) {
User user =
userRepository
.findByUsername(username)
.findByUsernameIgnoreCase(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
user.setApiKey(generateApiKey());
@@ -76,7 +100,7 @@ public class UserService implements UserServiceInterface {
public String getApiKeyForUser(String username) {
User user =
userRepository
.findByUsername(username)
.findByUsernameIgnoreCase(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return user.getApiKey();
}
@@ -90,9 +114,8 @@ public class UserService implements UserServiceInterface {
}
public UserDetails loadUserByApiKey(String apiKey) {
User userOptional = userRepository.findByApiKey(apiKey);
if (userOptional != null) {
User user = userOptional;
User user = userRepository.findByApiKey(apiKey);
if (user != null) {
// Convert your User entity to a UserDetails object with authorities
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
@@ -103,40 +126,58 @@ public class UserService implements UserServiceInterface {
}
public boolean validateApiKeyForUser(String username, String apiKey) {
Optional<User> userOpt = userRepository.findByUsername(username);
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey);
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
return userOpt.isPresent() && apiKey.equals(userOpt.get().getApiKey());
}
public void saveUser(String username, String password) {
public void saveUser(String username, AuthenticationType authenticationType)
throws IllegalArgumentException {
if (!isUsernameValid(username)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
User user = new User();
user.setUsername(username);
user.setEnabled(true);
user.setFirstLogin(false);
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
user.setAuthenticationType(authenticationType);
userRepository.save(user);
}
public void saveUser(String username, String password) throws IllegalArgumentException {
if (!isUsernameValid(username)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
User user = new User();
user.setUsername(username);
user.setPassword(passwordEncoder.encode(password));
user.setEnabled(true);
user.setAuthenticationType(AuthenticationType.WEB);
userRepository.save(user);
}
public void saveUser(String username, String password, String role, boolean firstLogin) {
public void saveUser(String username, String password, String role, boolean firstLogin)
throws IllegalArgumentException {
if (!isUsernameValid(username)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
User user = new User();
user.setUsername(username);
user.setPassword(passwordEncoder.encode(password));
user.addAuthority(new Authority(role, user));
user.setEnabled(true);
user.setAuthenticationType(AuthenticationType.WEB);
user.setFirstLogin(firstLogin);
userRepository.save(user);
}
public void saveUser(String username, String password, String role) {
User user = new User();
user.setUsername(username);
user.setPassword(passwordEncoder.encode(password));
user.addAuthority(new Authority(role, user));
user.setEnabled(true);
user.setFirstLogin(false);
userRepository.save(user);
public void saveUser(String username, String password, String role)
throws IllegalArgumentException {
saveUser(username, password, role, false);
}
public void deleteUser(String username) {
Optional<User> userOpt = userRepository.findByUsername(username);
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
if (userOpt.isPresent()) {
for (Authority authority : userOpt.get().getAuthorities()) {
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
@@ -151,18 +192,28 @@ public class UserService implements UserServiceInterface {
return userRepository.findByUsername(username).isPresent();
}
public boolean usernameExistsIgnoreCase(String username) {
return userRepository.findByUsernameIgnoreCase(username).isPresent();
}
public boolean hasUsers() {
return userRepository.count() > 0;
long userCount = userRepository.count();
if (userRepository
.findByUsernameIgnoreCase(Role.INTERNAL_API_USER.getRoleId())
.isPresent()) {
userCount -= 1;
}
return userCount > 0;
}
public void updateUserSettings(String username, Map<String, String> updates) {
Optional<User> userOpt = userRepository.findByUsername(username);
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
if (userOpt.isPresent()) {
User user = userOpt.get();
Map<String, String> settingsMap = user.getSettings();
if (settingsMap == null) {
settingsMap = new HashMap<String, String>();
settingsMap = new HashMap<>();
}
settingsMap.clear();
settingsMap.putAll(updates);
@@ -180,7 +231,14 @@ public class UserService implements UserServiceInterface {
return userRepository.findByUsernameIgnoreCase(username);
}
public void changeUsername(User user, String newUsername) {
public Authority findRole(User user) {
return authorityRepository.findByUserId(user.getId());
}
public void changeUsername(User user, String newUsername) throws IllegalArgumentException {
if (!isUsernameValid(newUsername)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
user.setUsername(newUsername);
userRepository.save(user);
}
@@ -195,11 +253,41 @@ public class UserService implements UserServiceInterface {
userRepository.save(user);
}
public void changeRole(User user, String newRole) {
Authority userAuthority = this.findRole(user);
userAuthority.setAuthority(newRole);
authorityRepository.save(userAuthority);
}
public boolean isPasswordCorrect(User user, String currentPassword) {
return passwordEncoder.matches(currentPassword, user.getPassword());
}
public boolean isUsernameValid(String username) {
return username.matches("[a-zA-Z0-9]+");
// Checks whether the simple username is formatted correctly
boolean isValidSimpleUsername =
username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$");
// Checks whether the email address is formatted correctly
boolean isValidEmail =
username.matches(
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
return isValidSimpleUsername || isValidEmail;
}
private String getInvalidUsernameMessage() {
return messageSource.getMessage(
"invalidUsernameMessage", null, LocaleContextHolder.getLocale());
}
public boolean hasPassword(String username) {
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
return user.isPresent() && user.get().hasPassword();
}
public boolean isAuthenticationTypeByUsername(
String username, AuthenticationType authenticationType) {
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
return user.isPresent()
&& authenticationType.name().equalsIgnoreCase(user.get().getAuthenticationType());
}
}

View File

@@ -0,0 +1,49 @@
package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class CustomOAuth2AuthenticationFailureHandler
extends SimpleUrlAuthenticationFailureHandler {
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2AuthenticationFailureHandler.class);
@Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
if (exception instanceof OAuth2AuthenticationException) {
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
String errorCode = error.getErrorCode();
if (error.getErrorCode().equals("Password must not be null")) {
errorCode = "userAlreadyExistsWeb";
}
logger.error("OAuth2 Authentication error: " + errorCode);
getRedirectStrategy()
.sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
return;
} else if (exception instanceof LockedException) {
logger.error("Account locked: ", exception);
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
} else {
logger.error("Unhandled authentication exception", exception);
super.onAuthenticationFailure(request, response, exception);
}
}
}

View File

@@ -0,0 +1,93 @@
package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.SavedRequest;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.utils.RequestUriUtils;
public class CustomOAuth2AuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler {
private LoginAttemptService loginAttemptService;
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2AuthenticationSuccessHandler.class);
private ApplicationProperties applicationProperties;
private UserService userService;
public CustomOAuth2AuthenticationSuccessHandler(
final LoginAttemptService loginAttemptService,
ApplicationProperties applicationProperties,
UserService userService) {
this.applicationProperties = applicationProperties;
this.userService = userService;
this.loginAttemptService = loginAttemptService;
}
@Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
// Get the saved request
HttpSession session = request.getSession(false);
SavedRequest savedRequest =
(session != null)
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
: null;
if (savedRequest != null
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
// Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication);
} else {
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
String username = oauthUser.getName();
if (loginAttemptService.isBlocked(username)) {
if (session != null) {
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
}
throw new LockedException(
"Your account has been locked due to too many failed login attempts.");
}
if (userService.usernameExistsIgnoreCase(username)
&& userService.hasPassword(username)
&& !userService.isAuthenticationTypeByUsername(
username, AuthenticationType.OAUTH2)
&& oAuth.getAutoCreateUser()) {
response.sendRedirect(
request.getContextPath() + "/logout?oauth2AuthenticationErrorWeb=true");
return;
} else {
try {
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
response.sendRedirect("/");
return;
} catch (IllegalArgumentException e) {
response.sendRedirect("/logout?invalidUsername=true");
return;
}
}
}
}
}

View File

@@ -0,0 +1,86 @@
package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2LogoutSuccessHandler.class);
private final SessionRegistry sessionRegistry;
private final ApplicationProperties applicationProperties;
public CustomOAuth2LogoutSuccessHandler(
ApplicationProperties applicationProperties, SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
this.applicationProperties = applicationProperties;
}
@Override
public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String param = "logout=true";
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
String provider = oauth.getProvider() != null ? oauth.getProvider() : "";
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
param = "erroroauth=oauth2AuthenticationErrorWeb";
} else if (request.getParameter("error") != null) {
param = "error=" + request.getParameter("error");
} else if (request.getParameter("erroroauth") != null) {
param = "erroroauth=" + request.getParameter("erroroauth");
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
param = "error=oauth2AutoCreateDisabled";
}
HttpSession session = request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry.removeSessionInformation(sessionId);
session.invalidate();
logger.debug("Session invalidated: " + sessionId);
}
switch (provider) {
case "keycloak":
String logoutUrl =
oauth.getIssuer()
+ "/protocol/openid-connect/logout"
+ "?client_id="
+ oauth.getClientId()
+ "&post_logout_redirect_uri="
+ response.encodeRedirectURL(
request.getScheme()
+ "://"
+ request.getHeader("host")
+ "/login?"
+ param);
logger.debug("Redirecting to Keycloak logout URL: " + logoutUrl);
response.sendRedirect(logoutUrl);
break;
case "google":
// Add Google specific logout URL if needed
default:
String redirectUrl = request.getContextPath() + "/login?" + param;
logger.debug("Redirecting to default logout URL: " + redirectUrl);
response.sendRedirect(redirectUrl);
break;
}
}
}

View File

@@ -0,0 +1,73 @@
package stirling.software.SPDF.config.security.oauth2;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.User;
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
private final OidcUserService delegate = new OidcUserService();
private UserService userService;
private LoginAttemptService loginAttemptService;
private ApplicationProperties applicationProperties;
private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2UserService.class);
public CustomOAuth2UserService(
ApplicationProperties applicationProperties,
UserService userService,
LoginAttemptService loginAttemptService) {
this.applicationProperties = applicationProperties;
this.userService = userService;
this.loginAttemptService = loginAttemptService;
}
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
String usernameAttribute =
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
try {
OidcUser user = delegate.loadUser(userRequest);
String username = user.getUserInfo().getClaimAsString(usernameAttribute);
Optional<User> duser = userService.findByUsernameIgnoreCase(username);
if (duser.isPresent()) {
if (loginAttemptService.isBlocked(username)) {
throw new LockedException(
"Your account has been locked due to too many failed login attempts.");
}
if (userService.hasPassword(username)) {
throw new IllegalArgumentException("Password must not be null");
}
}
// Return a new OidcUser with adjusted attributes
return new DefaultOidcUser(
user.getAuthorities(),
userRequest.getIdToken(),
user.getUserInfo(),
usernameAttribute);
} catch (java.lang.IllegalArgumentException e) {
logger.error("Error loading OIDC user: {}", e.getMessage());
throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);
} catch (Exception e) {
logger.error("Unexpected error loading OIDC user", e);
throw new OAuth2AuthenticationException("Unexpected error during authentication");
}
}
}

View File

@@ -0,0 +1,57 @@
package stirling.software.SPDF.config.security.oauth2;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import stirling.software.SPDF.model.ApplicationProperties;
public class CustomOAuthUserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
private static final Logger logger = LoggerFactory.getLogger(CustomOAuthUserService.class);
private final OidcUserService delegate = new OidcUserService();
private ApplicationProperties applicationProperties;
public CustomOAuthUserService(ApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties;
}
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
String usernameAttribute =
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
try {
OidcUser user = delegate.loadUser(userRequest);
Map<String, Object> attributes = new HashMap<>(user.getAttributes());
// Ensure the preferred username attribute is present
if (!attributes.containsKey(usernameAttribute)) {
attributes.put(usernameAttribute, attributes.getOrDefault("email", ""));
usernameAttribute = "email";
logger.info("Adjusted username attribute to use email");
}
// Return a new OidcUser with adjusted attributes
return new DefaultOidcUser(
user.getAuthorities(),
userRequest.getIdToken(),
user.getUserInfo(),
usernameAttribute);
} catch (java.lang.IllegalArgumentException e) {
throw new OAuth2AuthenticationException(
new OAuth2Error(e.getMessage()), e.getMessage(), e);
}
}
}

View File

@@ -27,7 +27,9 @@ import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.PdfMetadata;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@@ -49,6 +51,7 @@ public class SplitPDFController {
// open the pdf document
PDDocument document = Loader.loadPDF(file.getBytes());
PdfMetadata metadata = PdfUtils.extractMetadataFromPdf(document);
int totalPages = document.getNumberOfPages();
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
System.out.println(
@@ -75,6 +78,9 @@ public class SplitPDFController {
}
previousPageNumber = splitPoint + 1;
// Transfer metadata to split pdf
PdfUtils.setMetadataToPdf(splitDocument, metadata);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);

View File

@@ -4,8 +4,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -41,117 +39,137 @@ public class SplitPdfBySizeController {
+ " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF Output:ZIP-PDF Type:SISO")
public ResponseEntity<byte[]> autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request)
throws Exception {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<ByteArrayOutputStream>();
MultipartFile file = request.getFileInput();
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
// 0 = size, 1 = page count, 2 = doc count
int type = request.getSplitType();
String value = request.getSplitValue();
if (type == 0) { // Split by size
long maxBytes = GeneralUtils.convertSizeToBytes(value);
long currentSize = 0;
PDDocument currentDoc = new PDDocument();
for (PDPage page : sourceDocument.getPages()) {
ByteArrayOutputStream pageOutputStream = new ByteArrayOutputStream();
PDDocument tempDoc = new PDDocument();
tempDoc.addPage(page);
tempDoc.save(pageOutputStream);
tempDoc.close();
long pageSize = pageOutputStream.size();
if (currentSize + pageSize > maxBytes) {
// Save and reset current document
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
currentDoc = new PDDocument();
currentSize = 0;
}
currentDoc.addPage(page);
currentSize += pageSize;
}
// Add the last document if it contains any pages
if (currentDoc.getPages().getCount() != 0) {
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
}
} else if (type == 1) { // Split by page count
int pageCount = Integer.parseInt(value);
int currentPageCount = 0;
PDDocument currentDoc = new PDDocument();
for (PDPage page : sourceDocument.getPages()) {
currentDoc.addPage(page);
currentPageCount++;
if (currentPageCount == pageCount) {
// Save and reset current document
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
currentDoc = new PDDocument();
currentPageCount = 0;
}
}
// Add the last document if it contains any pages
if (currentDoc.getPages().getCount() != 0) {
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
}
} else if (type == 2) { // Split by doc count
int documentCount = Integer.parseInt(value);
int totalPageCount = sourceDocument.getNumberOfPages();
int pagesPerDocument = totalPageCount / documentCount;
int extraPages = totalPageCount % documentCount;
int currentPageIndex = 0;
for (int i = 0; i < documentCount; i++) {
PDDocument currentDoc = new PDDocument();
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
for (int j = 0; j < pagesToAdd; j++) {
currentDoc.addPage(sourceDocument.getPage(currentPageIndex++));
}
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
}
} else {
throw new IllegalArgumentException("Invalid argument for split type");
}
sourceDocument.close();
Path zipFile = Files.createTempFile("split_documents", ".zip");
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
byte[] data;
byte[] data = null;
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile));
PDDocument sourceDocument = Loader.loadPDF(file.getBytes())) {
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf";
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();
int type = request.getSplitType();
String value = request.getSplitValue();
ZipEntry pdfEntry = new ZipEntry(fileName);
zipOut.putNextEntry(pdfEntry);
zipOut.write(pdf);
zipOut.closeEntry();
if (type == 0) {
long maxBytes = GeneralUtils.convertSizeToBytes(value);
handleSplitBySize(sourceDocument, maxBytes, zipOut, filename);
} else if (type == 1) {
int pageCount = Integer.parseInt(value);
handleSplitByPageCount(sourceDocument, pageCount, zipOut, filename);
} else if (type == 2) {
int documentCount = Integer.parseInt(value);
handleSplitByDocCount(sourceDocument, documentCount, zipOut, filename);
} else {
throw new IllegalArgumentException("Invalid argument for split type");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
data = Files.readAllBytes(zipFile);
Files.delete(zipFile);
Files.deleteIfExists(zipFile);
}
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
}
private ByteArrayOutputStream currentDocToByteArray(PDDocument document) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
document.close();
return baos;
private void handleSplitBySize(
PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename)
throws IOException {
long currentSize = 0;
PDDocument currentDoc = new PDDocument();
int fileIndex = 1;
for (int pageIndex = 0; pageIndex < sourceDocument.getNumberOfPages(); pageIndex++) {
PDPage page = sourceDocument.getPage(pageIndex);
ByteArrayOutputStream pageOutputStream = new ByteArrayOutputStream();
try (PDDocument tempDoc = new PDDocument()) {
PDPage importedPage = tempDoc.importPage(page); // This creates a new PDPage object
tempDoc.save(pageOutputStream);
}
long pageSize = pageOutputStream.size();
if (currentSize + pageSize > maxBytes) {
if (currentDoc.getNumberOfPages() > 0) {
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
currentDoc.close(); // Make sure to close the document
currentDoc = new PDDocument();
currentSize = 0;
}
}
PDPage newPage = new PDPage(page.getCOSObject()); // Re-create the page
currentDoc.addPage(newPage);
currentSize += pageSize;
}
if (currentDoc.getNumberOfPages() != 0) {
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
currentDoc.close();
}
}
private void handleSplitByPageCount(
PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename)
throws IOException {
int currentPageCount = 0;
PDDocument currentDoc = new PDDocument();
int fileIndex = 1;
for (PDPage page : sourceDocument.getPages()) {
currentDoc.addPage(page);
currentPageCount++;
if (currentPageCount == pageCount) {
// Save and reset current document
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
currentDoc = new PDDocument();
currentPageCount = 0;
}
}
// Add the last document if it contains any pages
if (currentDoc.getPages().getCount() != 0) {
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
}
}
private void handleSplitByDocCount(
PDDocument sourceDocument,
int documentCount,
ZipOutputStream zipOut,
String baseFilename)
throws IOException {
int totalPageCount = sourceDocument.getNumberOfPages();
int pagesPerDocument = totalPageCount / documentCount;
int extraPages = totalPageCount % documentCount;
int currentPageIndex = 0;
int fileIndex = 1;
for (int i = 0; i < documentCount; i++) {
PDDocument currentDoc = new PDDocument();
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
for (int j = 0; j < pagesToAdd; j++) {
currentDoc.addPage(sourceDocument.getPage(currentPageIndex++));
}
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
}
}
private void saveDocumentToZip(
PDDocument document, ZipOutputStream zipOut, String baseFilename, int index)
throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
document.save(outStream);
document.close(); // Close the document to free resources
// Create a new zip entry
ZipEntry zipEntry = new ZipEntry(baseFilename + "_" + index + ".pdf");
zipOut.putNextEntry(zipEntry);
zipOut.write(outStream.toByteArray());
zipOut.closeEntry();
}
}

View File

@@ -43,12 +43,15 @@ public class UserController {
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@PostMapping("/register")
public String register(@ModelAttribute UsernameAndPass requestModel, Model model) {
if (userService.usernameExists(requestModel.getUsername())) {
if (userService.usernameExistsIgnoreCase(requestModel.getUsername())) {
model.addAttribute("error", "Username already exists");
return "register";
}
userService.saveUser(requestModel.getUsername(), requestModel.getPassword());
try {
userService.saveUser(requestModel.getUsername(), requestModel.getPassword());
} catch (IllegalArgumentException e) {
return "redirect:/login?messageType=invalidUsername";
}
return "redirect:/login?registered=true";
}
@@ -70,7 +73,8 @@ public class UserController {
return new RedirectView("/account?messageType=notAuthenticated");
}
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
// The username MUST be unique when renaming
Optional<User> userOpt = userService.findByUsername(principal.getName());
if (userOpt == null || userOpt.isEmpty()) {
return new RedirectView("/account?messageType=userNotFound");
@@ -91,13 +95,17 @@ public class UserController {
}
if (newUsername != null && newUsername.length() > 0) {
userService.changeUsername(user, newUsername);
try {
userService.changeUsername(user, newUsername);
} catch (IllegalArgumentException e) {
return new RedirectView("/account?messageType=invalidUsername");
}
}
// Logout using Spring's utility
new SecurityContextLogoutHandler().logout(request, response, null);
return new RedirectView("/login?messageType=credsUpdated");
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED);
}
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@@ -113,7 +121,7 @@ public class UserController {
return new RedirectView("/change-creds?messageType=notAuthenticated");
}
Optional<User> userOpt = userService.findByUsername(principal.getName());
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
if (userOpt == null || userOpt.isEmpty()) {
return new RedirectView("/change-creds?messageType=userNotFound");
@@ -130,7 +138,7 @@ public class UserController {
// Logout using Spring's utility
new SecurityContextLogoutHandler().logout(request, response, null);
return new RedirectView("/login?messageType=credsUpdated");
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED);
}
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@@ -146,7 +154,7 @@ public class UserController {
return new RedirectView("/account?messageType=notAuthenticated");
}
Optional<User> userOpt = userService.findByUsername(principal.getName());
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
if (userOpt == null || userOpt.isEmpty()) {
return new RedirectView("/account?messageType=userNotFound");
@@ -163,7 +171,7 @@ public class UserController {
// Logout using Spring's utility
new SecurityContextLogoutHandler().logout(request, response, null);
return new RedirectView("/login?messageType=credsUpdated");
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED);
}
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@@ -207,7 +215,7 @@ public class UserController {
return new RedirectView("/addUsers?messageType=usernameExists");
}
}
if (userService.usernameExists(username)) {
if (userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=usernameExists");
}
try {
@@ -226,12 +234,51 @@ public class UserController {
return new RedirectView("/addUsers"); // Redirect to account page after adding the user
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/admin/changeRole")
public RedirectView changeRole(
@RequestParam(name = "username") String username,
@RequestParam(name = "role") String role,
Authentication authentication) {
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
if (!userOpt.isPresent()) {
return new RedirectView("/addUsers?messageType=userNotFound");
}
if (!userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=userNotFound");
}
// Get the currently authenticated username
String currentUsername = authentication.getName();
// Check if the provided username matches the current session's username
if (currentUsername.equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=downgradeCurrentUser");
}
try {
// Validate the role
Role roleEnum = Role.fromString(role);
if (roleEnum == Role.INTERNAL_API_USER) {
// If the role is INTERNAL_API_USER, reject the request
return new RedirectView("/addUsers?messageType=invalidRole");
}
} catch (IllegalArgumentException e) {
// If the role ID is not valid, redirect with an error message
return new RedirectView("/addUsers?messageType=invalidRole");
}
User user = userOpt.get();
userService.changeRole(user, role);
return new RedirectView("/addUsers"); // Redirect to account page after adding the user
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/admin/deleteUser/{username}")
public RedirectView deleteUser(
@PathVariable(name = "username") String username, Authentication authentication) {
if (!userService.usernameExists(username)) {
if (!userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=deleteUsernameExists");
}
@@ -239,7 +286,7 @@ public class UserController {
String currentUsername = authentication.getName();
// Check if the provided username matches the current session's username
if (currentUsername.equals(username)) {
if (currentUsername.equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=deleteCurrentUser");
}
invalidateUserSessions(username);
@@ -291,4 +338,6 @@ public class UserController {
}
return ResponseEntity.ok(apiKey);
}
private static final String LOGIN_MESSAGETYPE_CREDSUPDATED = "/login?messageType=credsUpdated";
}

View File

@@ -6,10 +6,6 @@ import java.net.URLConnection;
import org.apache.pdfbox.rendering.ImageType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -39,8 +35,8 @@ public class ConvertImgPDFController {
summary = "Convert PDF to image(s)",
description =
"This endpoint converts a PDF file to image(s) with the specified image format, color type, and DPI. Users can choose to get a single image or multiple images. Input:PDF Output:Image Type:SI-Conditional")
public ResponseEntity<Resource> convertToImage(@ModelAttribute ConvertToImageRequest request)
throws IOException {
public ResponseEntity<byte[]> convertToImage(@ModelAttribute ConvertToImageRequest request)
throws NumberFormatException, Exception {
MultipartFile file = request.getFileInput();
String imageFormat = request.getImageFormat();
String singleOrMultiple = request.getSingleOrMultiple();
@@ -60,38 +56,28 @@ public class ConvertImgPDFController {
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
try {
result =
PdfUtils.convertFromPdf(
pdfBytes,
imageFormat.toUpperCase(),
colorTypeResult,
singleImage,
Integer.valueOf(dpi),
filename);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
result =
PdfUtils.convertFromPdf(
pdfBytes,
imageFormat.toUpperCase(),
colorTypeResult,
singleImage,
Integer.valueOf(dpi),
filename);
if(result == null || result.length == 0) {
logger.error("resultant bytes for {} is null, error converting ", filename);
}
if (singleImage) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
ResponseEntity<Resource> response =
new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK);
return response;
String docName = filename + "." + imageFormat;
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));
return WebResponseUtils.bytesToWebResponse(result, docName, mediaType);
} else {
ByteArrayResource resource = new ByteArrayResource(result);
// return the Resource in the response
return ResponseEntity.ok()
.header(
HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + filename + "_convertedToImages.zip")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(resource.contentLength())
.body(resource);
String zipFilename = filename + "_convertedToImages.zip";
return WebResponseUtils.bytesToWebResponse(
result, zipFilename, MediaType.APPLICATION_OCTET_STREAM);
}
}

View File

@@ -0,0 +1,32 @@
package stirling.software.SPDF.controller.api.converters;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.utils.PDFToFile;
@RestController
@Tag(name = "Convert", description = "Convert APIs")
@RequestMapping("/api/v1/convert")
public class ConvertPDFToHtml {
@PostMapping(consumes = "multipart/form-data", value = "/pdf/html")
@Operation(
summary = "Convert PDF to HTML",
description =
"This endpoint converts a PDF file to HTML format. Input:PDF Output:HTML Type:SISO")
public ResponseEntity<byte[]> processPdfToHTML(@ModelAttribute PDFFile request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToHtml(inputFile);
}
}

View File

@@ -29,18 +29,6 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Convert", description = "Convert APIs")
public class ConvertPDFToOffice {
@PostMapping(consumes = "multipart/form-data", value = "/pdf/html")
@Operation(
summary = "Convert PDF to HTML",
description =
"This endpoint converts a PDF file to HTML format. Input:PDF Output:HTML Type:SISO")
public ResponseEntity<byte[]> processPdfToHTML(@ModelAttribute PDFFile request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import");
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf/presentation")
@Operation(
summary = "Convert PDF to Presentation format",

View File

@@ -16,7 +16,7 @@ import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.model.api.converters.PdfToPdfARequest;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
@@ -31,8 +31,10 @@ public class ConvertPDFToPDFA {
summary = "Convert a PDF to a PDF/A",
description =
"This endpoint converts a PDF file to a PDF/A file. PDF/A is a format designed for long-term archiving of digital documents. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> pdfToPdfA(@ModelAttribute PDFFile request) throws Exception {
public ResponseEntity<byte[]> pdfToPdfA(@ModelAttribute PdfToPdfARequest request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
String outputFormat = request.getOutputFormat();
// Save the uploaded file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf");
@@ -47,7 +49,7 @@ public class ConvertPDFToPDFA {
command.add("--skip-text");
command.add("--tesseract-timeout=0");
command.add("--output-type");
command.add("pdfa");
command.add(outputFormat.toString());
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());

View File

@@ -6,8 +6,6 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@@ -28,10 +26,6 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert")
public class ConvertWebsiteToPDF {
@Autowired
@Qualifier("bookAndHtmlFormatsInstalled")
private boolean bookAndHtmlFormatsInstalled;
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
@Operation(
summary = "Convert a URL to a PDF",
@@ -53,11 +47,7 @@ public class ConvertWebsiteToPDF {
// Prepare the OCRmyPDF command
List<String> command = new ArrayList<>();
if (!bookAndHtmlFormatsInstalled) {
command.add("weasyprint");
} else {
command.add("wkhtmltopdf");
}
command.add("weasyprint");
command.add(URL);
command.add(tempOutputFile.toString());

View File

@@ -9,7 +9,6 @@ import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -143,8 +142,7 @@ public class ExtractImageScansController {
.runCommandWithOutputHandling(command);
// Read the output photos in temp directory
List<Path> tempOutputFiles =
Files.list(tempDir).sorted().collect(Collectors.toList());
List<Path> tempOutputFiles = Files.list(tempDir).sorted().toList();
for (Path tempOutputFile : tempOutputFiles) {
byte[] imageBytes = Files.readAllBytes(tempOutputFile);
processedImageBytes.add(imageBytes);
@@ -156,7 +154,7 @@ public class ExtractImageScansController {
// Create zip file if multiple images
if (processedImageBytes.size() > 1) {
String outputZipFilename =
fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip";
fileName.replaceFirst(REPLACEFIRST, "") + "_processed.zip";
tempZipFile = Files.createTempFile("output_", ".zip");
try (ZipOutputStream zipOut =
@@ -165,7 +163,7 @@ public class ExtractImageScansController {
for (int i = 0; i < processedImageBytes.size(); i++) {
ZipEntry entry =
new ZipEntry(
fileName.replaceFirst("[.][^.]+$", "")
fileName.replaceFirst(REPLACEFIRST, "")
+ "_"
+ (i + 1)
+ ".png");
@@ -187,7 +185,7 @@ public class ExtractImageScansController {
byte[] imageBytes = processedImageBytes.get(0);
return WebResponseUtils.bytesToWebResponse(
imageBytes,
fileName.replaceFirst("[.][^.]+$", "") + ".png",
fileName.replaceFirst(REPLACEFIRST, "") + ".png",
MediaType.IMAGE_PNG);
}
} finally {
@@ -219,4 +217,6 @@ public class ExtractImageScansController {
});
}
}
private static final String REPLACEFIRST = "[.][^.]+$";
}

View File

@@ -1,27 +1,29 @@
package stirling.software.SPDF.controller.api.misc;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.RescaleOp;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
@@ -29,6 +31,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@@ -39,6 +42,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@@ -48,98 +52,39 @@ public class FakeScanControllerWIP {
private static final Logger logger = LoggerFactory.getLogger(FakeScanControllerWIP.class);
// TODO
// TODO finish
@PostMapping(consumes = "multipart/form-data", value = "/fake-scan")
@Hidden
// @PostMapping(consumes = "multipart/form-data", value = "/fakeScan")
@Operation(
summary = "Repair a PDF file",
description =
"This endpoint repairs a given PDF file by running Ghostscript command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response.")
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request) throws IOException {
public ResponseEntity<byte[]> fakeScan(@ModelAttribute PDFFile request) throws IOException {
MultipartFile inputFile = request.getFileInput();
// Load the PDF document
PDDocument document = Loader.loadPDF(inputFile.getBytes());
PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
ImageIO.write(image, "png", new File("scanned-" + (page + 1) + ".png"));
PDFRenderer renderer = new PDFRenderer(document);
List<BufferedImage> images = new ArrayList<>();
// Convert each page to an image
for (int i = 0; i < document.getNumberOfPages(); i++) {
BufferedImage image = renderer.renderImageWithDPI(i, 150, ImageType.GRAY);
images.add(processImage(image));
}
document.close();
// Constants
int scannedness = 90; // Value between 0 and 100
int dirtiness = 0; // Value between 0 and 100
// Load the source image
BufferedImage sourceImage = ImageIO.read(new File("scanned-1.png"));
// Create the destination image
BufferedImage destinationImage =
new BufferedImage(
sourceImage.getWidth(), sourceImage.getHeight(), sourceImage.getType());
// Apply a brightness and contrast effect based on the "scanned-ness"
float scaleFactor = 1.0f + (scannedness / 100.0f) * 0.5f; // Between 1.0 and 1.5
float offset = scannedness * 1.5f; // Between 0 and 150
BufferedImageOp op = new RescaleOp(scaleFactor, offset, null);
op.filter(sourceImage, destinationImage);
// Apply a rotation effect
double rotationRequired =
Math.toRadians(
(new SecureRandom().nextInt(3 - 1)
+ 1)); // Random angle between 1 and 3 degrees
double locationX = destinationImage.getWidth() / 2;
double locationY = destinationImage.getHeight() / 2;
AffineTransform tx =
AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp rotateOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
destinationImage = rotateOp.filter(destinationImage, null);
// Apply a blur effect based on the "scanned-ness"
float blurIntensity = scannedness / 100.0f * 0.2f; // Between 0.0 and 0.2
float[] matrix = {
blurIntensity, blurIntensity, blurIntensity,
blurIntensity, blurIntensity, blurIntensity,
blurIntensity, blurIntensity, blurIntensity
};
BufferedImageOp blurOp =
new ConvolveOp(new Kernel(3, 3, matrix), ConvolveOp.EDGE_NO_OP, null);
destinationImage = blurOp.filter(destinationImage, null);
// Add noise to the image based on the "dirtiness"
Random random = new SecureRandom();
for (int y = 0; y < destinationImage.getHeight(); y++) {
for (int x = 0; x < destinationImage.getWidth(); x++) {
if (random.nextInt(100) < dirtiness) {
// Change the pixel color to black randomly based on the "dirtiness"
destinationImage.setRGB(x, y, Color.BLACK.getRGB());
}
}
}
// Save the image
ImageIO.write(destinationImage, "PNG", new File("scanned-1.png"));
PDDocument documentOut = new PDDocument();
for (int page = 1; page <= document.getNumberOfPages(); ++page) {
BufferedImage bim = ImageIO.read(new File("scanned-" + page + ".png"));
// Adjust the dimensions of the page
PDPage pdPage = new PDPage(new PDRectangle(bim.getWidth() - 1, bim.getHeight() - 1));
documentOut.addPage(pdPage);
PDImageXObject pdImage = LosslessFactory.createFromImage(documentOut, bim);
PDPageContentStream contentStream = new PDPageContentStream(documentOut, pdPage);
// Draw the image with a slight offset and enlarged dimensions
contentStream.drawImage(pdImage, -1, -1, bim.getWidth() + 2, bim.getHeight() + 2);
contentStream.close();
}
// Create a new PDF document with the processed images
ByteArrayOutputStream baos = new ByteArrayOutputStream();
documentOut.save(baos);
documentOut.close();
PDDocument newDocument = new PDDocument();
for (BufferedImage img : images) {
// PDPageContentStream contentStream = new PDPageContentStream(newDocument, new
// PDPage());
PDImageXObject pdImage = JPEGFactory.createFromImage(newDocument, img);
PdfUtils.addImageToDocument(newDocument, pdImage, "maintainAspectRatio", false);
}
newDocument.save(baos);
newDocument.close();
// Return the optimized PDF as a response
String outputFilename =
@@ -148,4 +93,232 @@ public class FakeScanControllerWIP {
+ "_scanned.pdf";
return WebResponseUtils.boasToWebResponse(baos, outputFilename);
}
public BufferedImage processImage(BufferedImage image) {
// Rotation
image = softenEdges(image, 50);
image = rotate(image, 1);
image = applyGaussianBlur(image, 0.5);
addGaussianNoise(image, 0.5);
image = linearStretch(image);
addDustAndHairs(image, 3);
return image;
}
private BufferedImage rotate(BufferedImage image, double rotation) {
double rotationRequired = Math.toRadians(rotation);
double locationX = image.getWidth() / 2;
double locationY = image.getHeight() / 2;
AffineTransform tx =
AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC);
return op.filter(image, null);
}
private BufferedImage applyGaussianBlur(BufferedImage image, double sigma) {
int radius = 3; // Fixed radius size for simplicity
int size = 2 * radius + 1;
float[] data = new float[size * size];
double sum = 0.0;
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
double xDistance = i * i;
double yDistance = j * j;
double g = Math.exp(-(xDistance + yDistance) / (2 * sigma * sigma));
data[(i + radius) * size + j + radius] = (float) g;
sum += g;
}
}
// Normalize the kernel
for (int i = 0; i < data.length; i++) {
data[i] /= sum;
}
Kernel kernel = new Kernel(size, size, data);
BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
return op.filter(image, null);
}
public BufferedImage softenEdges(BufferedImage image, int featherRadius) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(
RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(image, 0, 0, null);
g2.setComposite(AlphaComposite.DstIn);
// Top edge
g2.setPaint(
new GradientPaint(
0,
0,
new Color(0, 0, 0, 1f),
0,
featherRadius * 2,
new Color(0, 0, 0, 0f)));
g2.fillRect(0, 0, width, featherRadius);
// Bottom edge
g2.setPaint(
new GradientPaint(
0,
height - featherRadius * 2,
new Color(0, 0, 0, 0f),
0,
height,
new Color(0, 0, 0, 1f)));
g2.fillRect(0, height - featherRadius, width, featherRadius);
// Left edge
g2.setPaint(
new GradientPaint(
0,
0,
new Color(0, 0, 0, 1f),
featherRadius * 2,
0,
new Color(0, 0, 0, 0f)));
g2.fillRect(0, 0, featherRadius, height);
// Right edge
g2.setPaint(
new GradientPaint(
width - featherRadius * 2,
0,
new Color(0, 0, 0, 0f),
width,
0,
new Color(0, 0, 0, 1f)));
g2.fillRect(width - featherRadius, 0, featherRadius, height);
g2.dispose();
return output;
}
private void addDustAndHairs(BufferedImage image, float intensity) {
int width = image.getWidth();
int height = image.getHeight();
Graphics2D g2d = image.createGraphics();
Random random = new SecureRandom();
// Set rendering hints for better quality
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Calculate the number of artifacts based on intensity
int numSpots = (int) (intensity * 10);
int numHairs = (int) (intensity * 20);
// Add spots with more variable sizes
g2d.setColor(new Color(100, 100, 100, 50)); // Semi-transparent gray
for (int i = 0; i < numSpots; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int ovalSize = 1 + random.nextInt(3); // Base size + variable component
if (random.nextFloat() > 0.9) {
// 10% chance to get a larger spot
ovalSize += random.nextInt(3);
}
g2d.fill(new Ellipse2D.Double(x, y, ovalSize, ovalSize));
}
// Add hairs
g2d.setStroke(new BasicStroke(0.5f)); // Thin stroke for hairs
g2d.setColor(new Color(80, 80, 80, 40)); // Slightly lighter and more transparent
for (int i = 0; i < numHairs; i++) {
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = x1 + random.nextInt(20) - 10; // Random length and direction
int y2 = y1 + random.nextInt(20) - 10;
Path2D.Double hair = new Path2D.Double();
hair.moveTo(x1, y1);
hair.curveTo(x1, y1, (x1 + x2) / 2, (y1 + y2) / 2, x2, y2);
g2d.draw(hair);
}
g2d.dispose();
}
private void addGaussianNoise(BufferedImage image, double strength) {
Random rand = new SecureRandom();
int width = image.getWidth();
int height = image.getHeight();
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int rgba = image.getRGB(i, j);
int alpha = (rgba >> 24) & 0xff;
int red = (rgba >> 16) & 0xff;
int green = (rgba >> 8) & 0xff;
int blue = rgba & 0xff;
// Apply Gaussian noise
red = (int) (red + rand.nextGaussian() * strength);
green = (int) (green + rand.nextGaussian() * strength);
blue = (int) (blue + rand.nextGaussian() * strength);
// Clamping values to the 0-255 range
red = Math.min(Math.max(0, red), 255);
green = Math.min(Math.max(0, green), 255);
blue = Math.min(Math.max(0, blue), 255);
image.setRGB(i, j, (alpha << 24) | (red << 16) | (green << 8) | blue);
}
}
}
public BufferedImage linearStretch(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int min = 255;
int max = 0;
// First pass: find the min and max grayscale values
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = image.getRGB(x, y);
int gray =
(int)
(((rgb >> 16) & 0xff) * 0.299
+ ((rgb >> 8) & 0xff) * 0.587
+ (rgb & 0xff) * 0.114); // Convert to grayscale
if (gray < min) min = gray;
if (gray > max) max = gray;
}
}
// Second pass: stretch the histogram
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = image.getRGB(x, y);
int alpha = (rgb >> 24) & 0xff;
int red = (rgb >> 16) & 0xff;
int green = (rgb >> 8) & 0xff;
int blue = rgb & 0xff;
// Apply linear stretch to each channel
red = (int) (((red - min) / (float) (max - min)) * 255);
green = (int) (((green - min) / (float) (max - min)) * 255);
blue = (int) (((blue - min) / (float) (max - min)) * 255);
// Set new RGB value maintaining the alpha channel
rgb = (alpha << 24) | (red << 16) | (green << 8) | blue;
image.setRGB(x, y, rgb);
}
}
return image;
}
}

View File

@@ -0,0 +1,84 @@
package stirling.software.SPDF.controller.api.misc;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.PdfMetadata;
import stirling.software.SPDF.model.api.misc.FlattenRequest;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs")
public class FlattenController {
@PostMapping(consumes = "multipart/form-data", value = "/flatten")
@Operation(
summary = "Flatten PDF form fields or full page",
description =
"Flattening just PDF form fields or converting each page to images to make text unselectable. Input: PDF, Output: PDF. Type: SISO")
public ResponseEntity<byte[]> flatten(@ModelAttribute FlattenRequest request) throws Exception {
MultipartFile file = request.getFileInput();
PDDocument document = Loader.loadPDF(file.getBytes());
PdfMetadata metadata = PdfUtils.extractMetadataFromPdf(document);
Boolean flattenOnlyForms = request.getFlattenOnlyForms();
if (Boolean.TRUE.equals(flattenOnlyForms)) {
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
acroForm.flatten();
}
return WebResponseUtils.pdfDocToWebResponse(
document, Filenames.toSimpleFileName(file.getOriginalFilename()));
} else {
// flatten whole page aka convert each page to image and readd it (making text
// unselectable)
PDFRenderer pdfRenderer = new PDFRenderer(document);
PDDocument newDocument = new PDDocument();
int numPages = document.getNumberOfPages();
for (int i = 0; i < numPages; i++) {
try {
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300, ImageType.RGB);
PDPage page = new PDPage();
page.setMediaBox(document.getPage(i).getMediaBox());
newDocument.addPage(page);
try (PDPageContentStream contentStream =
new PDPageContentStream(newDocument, page)) {
PDImageXObject pdImage = JPEGFactory.createFromImage(newDocument, image);
float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight();
contentStream.drawImage(pdImage, 0, 0, pageWidth, pageHeight);
}
} catch (IOException e) {
e.printStackTrace();
}
}
PdfUtils.setMetadataToPdf(newDocument, metadata);
return WebResponseUtils.pdfDocToWebResponse(
newDocument, Filenames.toSimpleFileName(file.getOriginalFilename()));
}
}
}

View File

@@ -0,0 +1,105 @@
package stirling.software.SPDF.controller.api.misc;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.IOException;
import java.util.Arrays;
import javax.imageio.ImageIO;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.misc.PrintFileRequest;
@RestController
@RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs")
public class PrintFileController {
// TODO
// @PostMapping(value = "/print-file", consumes = "multipart/form-data")
// @Operation(
// summary = "Prints PDF/Image file to a set printer",
// description =
// "Input of PDF or Image along with a printer name/URL/IP to match against to
// send it to (Fire and forget) Input:Any Output:N/A Type:SISO")
public ResponseEntity<String> printFile(@ModelAttribute PrintFileRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
String printerName = request.getPrinterName();
String contentType = file.getContentType();
try {
// Find matching printer
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
PrintService selectedService =
Arrays.stream(services)
.filter(
service ->
service.getName().toLowerCase().contains(printerName))
.findFirst()
.orElseThrow(
() ->
new IllegalArgumentException(
"No matching printer found"));
System.out.println("Selected Printer: " + selectedService.getName());
if ("application/pdf".equals(contentType)) {
PDDocument document = Loader.loadPDF(file.getBytes());
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintService(selectedService);
job.setPageable(new PDFPageable(document));
job.print();
document.close();
} else if (contentType.startsWith("image/")) {
BufferedImage image = ImageIO.read(file.getInputStream());
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintService(selectedService);
job.setPrintable(
new Printable() {
public int print(
Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
if (pageIndex != 0) {
return NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D) graphics;
g2d.translate(
pageFormat.getImageableX(), pageFormat.getImageableY());
g2d.drawImage(
image,
0,
0,
(int) pageFormat.getImageableWidth(),
(int) pageFormat.getImageableHeight(),
null);
return PAGE_EXISTS;
}
});
job.print();
}
return new ResponseEntity<>(
"File printed successfully to " + selectedService.getName(), HttpStatus.OK);
} catch (Exception e) {
System.err.println("Failed to print: " + e.getMessage());
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
}

View File

@@ -26,7 +26,6 @@ import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation;
@@ -36,7 +35,6 @@ public class PipelineDirectoryProcessor {
private static final Logger logger = LoggerFactory.getLogger(PipelineDirectoryProcessor.class);
@Autowired private ObjectMapper objectMapper;
@Autowired private ApiDocService apiDocService;
@Autowired private ApplicationProperties applicationProperties;
final String watchedFoldersDir = "./pipeline/watchedFolders/";
final String finishedFoldersDir = "./pipeline/finishedFolders/";
@@ -45,9 +43,6 @@ public class PipelineDirectoryProcessor {
@Scheduled(fixedRate = 60000)
public void scanFolders() {
if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) {
return;
}
Path watchedFolderPath = Paths.get(watchedFoldersDir);
if (!Files.exists(watchedFolderPath)) {
try {

View File

@@ -139,25 +139,29 @@ public class SanitizeController {
for (PDPage page : allPages) {
PDResources res = page.getResources();
// Remove embedded files from the PDF
res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles"));
if (res != null && res.getCOSObject() != null) {
res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles"));
}
}
}
private void sanitizeMetadata(PDDocument document) {
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
if (metadata != null) {
document.getDocumentCatalog().setMetadata(null);
if (document.getDocumentCatalog() != null) {
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
if (metadata != null) {
document.getDocumentCatalog().setMetadata(null);
}
}
}
private void sanitizeLinks(PDDocument document) throws IOException {
for (PDPage page : document.getPages()) {
for (PDAnnotation annotation : page.getAnnotations()) {
if (annotation instanceof PDAnnotationLink) {
if (annotation != null && annotation instanceof PDAnnotationLink) {
PDAction action = ((PDAnnotationLink) annotation).getAction();
if (action instanceof PDActionLaunch || action instanceof PDActionURI) {
if (action != null
&& (action instanceof PDActionLaunch
|| action instanceof PDActionURI)) {
((PDAnnotationLink) annotation).setAction(null);
}
}
@@ -167,7 +171,11 @@ public class SanitizeController {
private void sanitizeFonts(PDDocument document) {
for (PDPage page : document.getPages()) {
page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font"));
if (page != null
&& page.getResources() != null
&& page.getResources().getCOSObject() != null) {
page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font"));
}
}
}
}

View File

@@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@@ -19,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.Authority;
import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.User;
@@ -28,17 +30,65 @@ import stirling.software.SPDF.repository.UserRepository;
@Tag(name = "Account Security", description = "Account Security APIs")
public class AccountWebController {
@Autowired ApplicationProperties applicationProperties;
@GetMapping("/login")
public String login(HttpServletRequest request, Model model, Authentication authentication) {
if (authentication != null && authentication.isAuthenticated()) {
return "redirect:/";
}
model.addAttribute(
"oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled());
model.addAttribute("currentPage", "login");
if (request.getParameter("error") != null) {
String error = request.getParameter("error");
if (error != null) {
model.addAttribute("error", request.getParameter("error"));
switch (error) {
case "badcredentials":
error = "login.invalid";
break;
case "locked":
error = "login.locked";
break;
case "oauth2AuthenticationError":
error = "userAlreadyExistsOAuthMessage";
break;
default:
break;
}
model.addAttribute("error", error);
}
String erroroauth = request.getParameter("erroroauth");
if (erroroauth != null) {
switch (erroroauth) {
case "oauth2AutoCreateDisabled":
erroroauth = "login.oauth2AutoCreateDisabled";
break;
case "invalidUsername":
erroroauth = "login.invalid";
break;
case "userAlreadyExistsWeb":
erroroauth = "userAlreadyExistsWebMessage";
break;
case "oauth2AuthenticationErrorWeb":
erroroauth = "login.oauth2InvalidUserType";
break;
case "invalid_token_response":
erroroauth = "login.oauth2InvalidTokenResponse";
default:
break;
}
model.addAttribute("erroroauth", erroroauth);
}
if (request.getParameter("messageType") != null) {
model.addAttribute("messageType", "changedCredsMessage");
}
if (request.getParameter("logout") != null) {
@@ -53,7 +103,8 @@ public class AccountWebController {
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/addUsers")
public String showAddUserForm(Model model, Authentication authentication) {
public String showAddUserForm(
HttpServletRequest request, Model model, Authentication authentication) {
List<User> allUsers = userRepository.findAll();
Iterator<User> iterator = allUsers.iterator();
Map<String, String> roleDetails = Role.getAllRoleDetails();
@@ -71,6 +122,52 @@ public class AccountWebController {
}
}
String messageType = request.getParameter("messageType");
String deleteMessage = null;
if (messageType != null) {
switch (messageType) {
case "deleteCurrentUser":
deleteMessage = "deleteCurrentUserMessage";
break;
case "deleteUsernameExists":
deleteMessage = "deleteUsernameExistsMessage";
break;
default:
break;
}
model.addAttribute("deleteMessage", deleteMessage);
String addMessage = null;
switch (messageType) {
case "usernameExists":
addMessage = "usernameExistsMessage";
break;
case "invalidUsername":
addMessage = "invalidUsernameMessage";
break;
default:
break;
}
model.addAttribute("addMessage", addMessage);
}
String changeMessage = null;
if (messageType != null) {
switch (messageType) {
case "userNotFound":
changeMessage = "userNotFoundMessage";
break;
case "downgradeCurrentUser":
changeMessage = "downgradeCurrentUserMessage";
break;
default:
break;
}
model.addAttribute("changeMessage", changeMessage);
}
model.addAttribute("users", allUsers);
model.addAttribute("currentUsername", authentication.getName());
model.addAttribute("roleDetails", roleDetails);
@@ -85,17 +182,33 @@ public class AccountWebController {
}
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
String username = null;
if (principal instanceof UserDetails) {
// Cast the principal object to UserDetails
UserDetails userDetails = (UserDetails) principal;
// Retrieve username and other attributes
String username = userDetails.getUsername();
username = userDetails.getUsername();
// Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", false);
}
if (principal instanceof OAuth2User) {
// Cast the principal object to OAuth2User
OAuth2User userDetails = (OAuth2User) principal;
// Retrieve username and other attributes
username =
userDetails.getAttribute(
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername());
// Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", true);
}
if (username != null) {
// Fetch user details from the database
Optional<User> user =
userRepository.findByUsername(
userRepository.findByUsernameIgnoreCase(
username); // Assuming findByUsername method exists
if (!user.isPresent()) {
// Handle error appropriately
@@ -113,6 +226,30 @@ public class AccountWebController {
return "redirect:/error"; // Example redirection in case of error
}
String messageType = request.getParameter("messageType");
if (messageType != null) {
switch (messageType) {
case "notAuthenticated":
messageType = "notAuthenticatedMessage";
break;
case "userNotFound":
messageType = "userNotFoundMessage";
break;
case "incorrectPassword":
messageType = "incorrectPasswordMessage";
break;
case "usernameExists":
messageType = "usernameExistsMessage";
break;
case "invalidUsername":
messageType = "invalidUsernameMessage";
break;
default:
break;
}
model.addAttribute("messageType", messageType);
}
// Add attributes to the model
model.addAttribute("username", username);
model.addAttribute("role", user.get().getRolesAsString());
@@ -145,12 +282,34 @@ public class AccountWebController {
// Fetch user details from the database
Optional<User> user =
userRepository.findByUsername(
userRepository.findByUsernameIgnoreCase(
username); // Assuming findByUsername method exists
if (!user.isPresent()) {
// Handle error appropriately
return "redirect:/error"; // Example redirection in case of error
}
String messageType = request.getParameter("messageType");
if (messageType != null) {
switch (messageType) {
case "notAuthenticated":
messageType = "notAuthenticatedMessage";
break;
case "userNotFound":
messageType = "userNotFoundMessage";
break;
case "incorrectPassword":
messageType = "incorrectPasswordMessage";
break;
case "usernameExists":
messageType = "usernameExistsMessage";
break;
default:
break;
}
model.addAttribute("messageType", messageType);
}
// Add attributes to the model
model.addAttribute("username", username);
}

View File

@@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
@Controller
@Tag(name = "Misc", description = "Miscellaneous APIs")
public class OtherWebController {
@GetMapping("/compress-pdf")
@Hidden
public String compressPdfForm(Model model) {
@@ -53,6 +54,13 @@ public class OtherWebController {
return "misc/add-page-numbers";
}
@GetMapping("/fake-scan")
@Hidden
public String fakeScanForm(Model model) {
model.addAttribute("currentPage", "fake-scan");
return "misc/fake-scan";
}
@GetMapping("/extract-images")
@Hidden
public String extractImagesForm(Model model) {
@@ -81,6 +89,13 @@ public class OtherWebController {
return "misc/compare";
}
@GetMapping("/print-file")
@Hidden
public String printFileForm(Model model) {
model.addAttribute("currentPage", "print-file");
return "misc/print-file";
}
public List<String> getAvailableTesseractLanguages() {
String tessdataDir = "/usr/share/tessdata";
File[] files = new File(tessdataDir).listFiles();

View File

@@ -1,6 +1,10 @@
package stirling.software.SPDF.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@@ -118,6 +122,7 @@ public class ApplicationProperties {
private Boolean enableLogin;
private Boolean csrfDisabled;
private InitialLogin initialLogin;
private OAUTH2 oauth2;
private int loginAttemptCount;
private long loginResetTimeMinutes;
@@ -145,6 +150,14 @@ public class ApplicationProperties {
this.initialLogin = initialLogin;
}
public OAUTH2 getOAUTH2() {
return oauth2 != null ? oauth2 : new OAUTH2();
}
public void setOAUTH2(OAUTH2 oauth2) {
this.oauth2 = oauth2;
}
public Boolean getEnableLogin() {
return enableLogin;
}
@@ -165,6 +178,8 @@ public class ApplicationProperties {
public String toString() {
return "Security [enableLogin="
+ enableLogin
+ ", oauth2="
+ oauth2
+ ", initialLogin="
+ initialLogin
+ ", csrfDisabled="
@@ -202,14 +217,143 @@ public class ApplicationProperties {
+ "]";
}
}
public static class OAUTH2 {
private boolean enabled;
private String issuer;
private String clientId;
private String clientSecret;
private boolean autoCreateUser;
private String useAsUsername;
private String provider;
private Collection<String> scopes = new ArrayList<String>();
public boolean getEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public boolean getAutoCreateUser() {
return autoCreateUser;
}
public void setAutoCreateUser(boolean autoCreateUser) {
this.autoCreateUser = autoCreateUser;
}
public String getUseAsUsername() {
if (useAsUsername != null && useAsUsername.trim().length() > 0) {
return useAsUsername;
}
return "email";
}
public void setUseAsUsername(String useAsUsername) {
this.useAsUsername = useAsUsername;
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public Collection<String> getScopes() {
return scopes;
}
public void setScopes(String scpoes) {
List<String> scopesList =
Arrays.stream(scpoes.split(","))
.map(String::trim)
.collect(Collectors.toList());
this.scopes.addAll(scopesList);
}
@Override
public String toString() {
return "OAUTH2 [enabled="
+ enabled
+ ", issuer="
+ issuer
+ ", clientId="
+ clientId
+ ", clientSecret="
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
+ ", autoCreateUser="
+ autoCreateUser
+ ", useAsUsername="
+ useAsUsername
+ ", provider"
+ provider
+ ", scopes="
+ scopes
+ "]";
}
}
}
public static class System {
private String defaultLocale;
private Boolean googlevisibility;
private String rootURIPath;
private String customStaticFilePath;
private Integer maxFileSize;
private boolean showUpdate;
private Boolean showUpdateOnlyAdmin;
private boolean customHTMLFiles;
public boolean isCustomHTMLFiles() {
return customHTMLFiles;
}
public void setCustomHTMLFiles(boolean customHTMLFiles) {
this.customHTMLFiles = customHTMLFiles;
}
public boolean getShowUpdateOnlyAdmin() {
return showUpdateOnlyAdmin;
}
public void setShowUpdateOnlyAdmin(boolean showUpdateOnlyAdmin) {
this.showUpdateOnlyAdmin = showUpdateOnlyAdmin;
}
public boolean getShowUpdate() {
return showUpdate;
}
public void setShowUpdate(boolean showUpdate) {
this.showUpdate = showUpdate;
}
private Boolean enableAlphaFunctionality;
@@ -237,29 +381,8 @@ public class ApplicationProperties {
this.googlevisibility = googlevisibility;
}
public String getRootURIPath() {
return rootURIPath;
}
public void setRootURIPath(String rootURIPath) {
this.rootURIPath = rootURIPath;
}
public String getCustomStaticFilePath() {
return customStaticFilePath;
}
public void setCustomStaticFilePath(String customStaticFilePath) {
this.customStaticFilePath = customStaticFilePath;
}
public Integer getMaxFileSize() {
return maxFileSize;
}
public void setMaxFileSize(Integer maxFileSize) {
this.maxFileSize = maxFileSize;
}
@Override
public String toString() {
@@ -267,14 +390,12 @@ public class ApplicationProperties {
+ defaultLocale
+ ", googlevisibility="
+ googlevisibility
+ ", rootURIPath="
+ rootURIPath
+ ", customStaticFilePath="
+ customStaticFilePath
+ ", maxFileSize="
+ maxFileSize
+ ", enableAlphaFunctionality="
+ enableAlphaFunctionality
+ ", showUpdate="
+ showUpdate
+ ", showUpdateOnlyAdmin="
+ showUpdateOnlyAdmin
+ "]";
}
}

View File

@@ -5,7 +5,7 @@ public class AttemptCounter {
private long lastAttemptTime;
public AttemptCounter() {
this.attemptCount = 1;
this.attemptCount = 0;
this.lastAttemptTime = System.currentTimeMillis();
}
@@ -18,11 +18,16 @@ public class AttemptCounter {
return attemptCount;
}
public long getlastAttemptTime() {
public long getLastAttemptTime() {
return lastAttemptTime;
}
public boolean shouldReset(long ATTEMPT_INCREMENT_TIME) {
return System.currentTimeMillis() - lastAttemptTime > ATTEMPT_INCREMENT_TIME;
public boolean shouldReset(long attemptIncrementTime) {
return System.currentTimeMillis() - lastAttemptTime > attemptIncrementTime;
}
public void reset() {
this.attemptCount = 0;
this.lastAttemptTime = System.currentTimeMillis();
}
}

View File

@@ -0,0 +1,6 @@
package stirling.software.SPDF.model;
public enum AuthenticationType {
WEB,
OAUTH2
}

View File

@@ -0,0 +1,19 @@
package stirling.software.SPDF.model;
import java.util.Calendar;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class PdfMetadata {
private String author;
private String producer;
private String title;
private String creator;
private String subject;
private String keywords;
private Calendar creationDate;
private Calendar modificationDate;
}

View File

@@ -47,6 +47,9 @@ public class User {
@Column(name = "roleName")
private String roleName;
@Column(name = "authenticationtype")
private String authenticationType;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
private Set<Authority> authorities = new HashSet<>();
@@ -116,6 +119,14 @@ public class User {
this.enabled = enabled;
}
public void setAuthenticationType(AuthenticationType authenticationType) {
this.authenticationType = authenticationType.toString().toLowerCase();
}
public String getAuthenticationType() {
return authenticationType;
}
public Set<Authority> getAuthorities() {
return authorities;
}
@@ -137,4 +148,8 @@ public class User {
.map(Authority::getAuthority)
.collect(Collectors.joining(", "));
}
public boolean hasPassword() {
return this.password != null && !this.password.isEmpty();
}
}

View File

@@ -0,0 +1,17 @@
package stirling.software.SPDF.model.api.converters;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class PdfToPdfARequest extends PDFFile {
@Schema(
description = "The output PDF/A type",
allowableValues = {"pdfa", "pdfa-1"})
private String outputFormat;
}

View File

@@ -0,0 +1,17 @@
package stirling.software.SPDF.model.api.misc;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class FlattenRequest extends PDFFile {
@Schema(
description =
"True to flatten only the forms, false to flatten full PDF (Convert page to image)")
private Boolean flattenOnlyForms;
}

View File

@@ -0,0 +1,15 @@
package stirling.software.SPDF.model.api.misc;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class PrintFileRequest extends PDFFile {
@Schema(description = "Name of printer to match against", required = true)
private String printerName;
}

View File

@@ -19,6 +19,16 @@ public class TextFinder extends PDFTextStripper {
private final boolean wholeWordSearch;
private final List<PDFText> textOccurrences = new ArrayList<>();
private class MatchInfo {
int startIndex;
int matchLength;
MatchInfo(int startIndex, int matchLength) {
this.startIndex = startIndex;
this.matchLength = matchLength;
}
}
public TextFinder(String searchText, boolean useRegex, boolean wholeWordSearch)
throws IOException {
this.searchText = searchText.toLowerCase();
@@ -27,36 +37,37 @@ public class TextFinder extends PDFTextStripper {
setSortByPosition(true);
}
private List<Integer> findOccurrencesInText(String searchText, String content) {
List<Integer> indexes = new ArrayList<>();
private List<MatchInfo> findOccurrencesInText(String searchText, String content) {
List<MatchInfo> matches = new ArrayList<>();
Pattern pattern;
if (useRegex) {
// Use regex-based search
pattern =
wholeWordSearch
? Pattern.compile("(\\b|_|\\.)" + searchText + "(\\b|_|\\.)")
? Pattern.compile("\\b" + searchText + "\\b")
: Pattern.compile(searchText);
} else {
// Use normal text search
pattern =
wholeWordSearch
? Pattern.compile(
"(\\b|_|\\.)" + Pattern.quote(searchText) + "(\\b|_|\\.)")
? Pattern.compile("\\b" + Pattern.quote(searchText) + "\\b")
: Pattern.compile(Pattern.quote(searchText));
}
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
indexes.add(matcher.start());
matches.add(new MatchInfo(matcher.start(), matcher.end() - matcher.start()));
}
return indexes;
return matches;
}
@Override
protected void writeString(String text, List<TextPosition> textPositions) {
for (Integer index : findOccurrencesInText(searchText, text.toLowerCase())) {
if (index + searchText.length() <= textPositions.size()) {
for (MatchInfo match : findOccurrencesInText(searchText, text.toLowerCase())) {
int index = match.startIndex;
if (index + match.matchLength <= textPositions.size()) {
// Initial values based on the first character
TextPosition first = textPositions.get(index);
float minX = first.getX();
@@ -65,7 +76,7 @@ public class TextFinder extends PDFTextStripper {
float maxY = first.getY() + first.getHeight();
// Loop over the rest of the characters and adjust bounding box values
for (int i = index; i < index + searchText.length(); i++) {
for (int i = index; i < index + match.matchLength; i++) {
TextPosition position = textPositions.get(i);
minX = Math.min(minX, position.getX());
minY = Math.min(minY, position.getY());

View File

@@ -9,4 +9,6 @@ import stirling.software.SPDF.model.Authority;
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
// Set<Authority> findByUsername(String username);
Set<Authority> findByUser_Username(String username);
Authority findByUserId(long user_id);
}

View File

@@ -3,8 +3,6 @@ package stirling.software.SPDF.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import stirling.software.SPDF.model.User;

View File

@@ -88,6 +88,7 @@ public class GeneralUtils {
}
sizeStr = sizeStr.trim().toUpperCase();
sizeStr = sizeStr.replace(",", ".").replace(" ", "");
try {
if (sizeStr.endsWith("KB")) {
return (long)
@@ -185,7 +186,7 @@ public class GeneralUtils {
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
// Now replace 'n' with its current value
return withMultiplication.replaceAll("n", String.valueOf(nValue));
return withMultiplication.replace("n", String.valueOf(nValue));
}
private static List<Integer> handlePart(String part, int totalPages, int offset) {

View File

@@ -25,6 +25,71 @@ import io.github.pixee.security.Filenames;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
public class PDFToFile {
public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile)
throws IOException, InterruptedException {
if (!"application/pdf".equals(inputFile.getContentType())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
// Get the original PDF file name without the extension
String originalPdfFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
String pdfBaseName = originalPdfFileName.substring(0, originalPdfFileName.lastIndexOf('.'));
Path tempInputFile = null;
Path tempOutputDir = null;
byte[] fileBytes;
String fileName = "temp.file";
try {
// Save the uploaded file to a temporary location
tempInputFile = Files.createTempFile("input_", ".pdf");
Files.copy(
inputFile.getInputStream(), tempInputFile, StandardCopyOption.REPLACE_EXISTING);
// Prepare the output directory
tempOutputDir = Files.createTempDirectory("output_");
// Run the pdftohtml command with complex output
List<String> command =
new ArrayList<>(
Arrays.asList(
"pdftohtml", "-c", tempInputFile.toString(), pdfBaseName));
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.PDFTOHTML)
.runCommandWithOutputHandling(command, tempOutputDir.toFile());
// Get output files
List<File> outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles());
// Return output files in a ZIP archive
fileName = pdfBaseName + "ToHtml.zip";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
for (File outputFile : outputFiles) {
ZipEntry entry = new ZipEntry(outputFile.getName());
zipOutputStream.putNextEntry(entry);
FileInputStream fis = new FileInputStream(outputFile);
IOUtils.copy(fis, zipOutputStream);
fis.close();
zipOutputStream.closeEntry();
}
zipOutputStream.close();
fileBytes = byteArrayOutputStream.toByteArray();
} finally {
// Clean up the temporary files
if (tempInputFile != null) Files.delete(tempInputFile);
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
}
return WebResponseUtils.bytesToWebResponse(
fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
}
public ResponseEntity<byte[]> processPdfToOfficeFormat(
MultipartFile inputFile, String outputFormat, String libreOfficeFilter)
throws IOException, InterruptedException {
@@ -39,17 +104,7 @@ public class PDFToFile {
// Validate output format
List<String> allowedFormats =
Arrays.asList(
"doc",
"docx",
"odt",
"ppt",
"pptx",
"odp",
"rtf",
"html",
"xml",
"txt:Text");
Arrays.asList("doc", "docx", "odt", "ppt", "pptx", "odp", "rtf", "xml", "txt:Text");
if (!allowedFormats.contains(outputFormat)) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}

View File

@@ -6,6 +6,7 @@ import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -19,11 +20,8 @@ import javax.imageio.stream.ImageOutputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
@@ -39,6 +37,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import stirling.software.SPDF.model.PdfMetadata;
public class PdfUtils {
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
@@ -336,14 +336,12 @@ public class PdfUtils {
}
}
private static void addImageToDocument(
public static void addImageToDocument(
PDDocument doc, PDImageXObject image, String fitOption, boolean autoRotate)
throws IOException {
boolean imageIsLandscape = image.getWidth() > image.getHeight();
PDRectangle pageSize = PDRectangle.A4;
System.out.println(fitOption);
if (autoRotate && imageIsLandscape) {
pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth());
}
@@ -421,4 +419,28 @@ public class PdfUtils {
logger.info("PDF successfully saved to byte array");
return baos.toByteArray();
}
public static PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
return PdfMetadata.builder()
.author(pdf.getDocumentInformation().getAuthor())
.producer(pdf.getDocumentInformation().getProducer())
.title(pdf.getDocumentInformation().getTitle())
.creator(pdf.getDocumentInformation().getCreator())
.subject(pdf.getDocumentInformation().getSubject())
.keywords(pdf.getDocumentInformation().getKeywords())
.creationDate(pdf.getDocumentInformation().getCreationDate())
.modificationDate(pdf.getDocumentInformation().getModificationDate())
.build();
}
public static void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata) {
pdf.getDocumentInformation().setAuthor(pdfMetadata.getAuthor());
pdf.getDocumentInformation().setProducer(pdfMetadata.getProducer());
pdf.getDocumentInformation().setTitle(pdfMetadata.getTitle());
pdf.getDocumentInformation().setCreator(pdfMetadata.getCreator());
pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject());
pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords());
pdf.getDocumentInformation().setCreationDate(pdfMetadata.getCreationDate());
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
}
}

View File

@@ -24,6 +24,7 @@ public class ProcessExecutor {
public enum Processes {
LIBRE_OFFICE,
PDFTOHTML,
OCR_MY_PDF,
PYTHON_OPENCV,
GHOSTSCRIPT,
@@ -45,6 +46,7 @@ public class ProcessExecutor {
int semaphoreLimit =
switch (key) {
case LIBRE_OFFICE -> 1;
case PDFTOHTML -> 1;
case OCR_MY_PDF -> 2;
case PYTHON_OPENCV -> 8;
case GHOSTSCRIPT -> 16;
@@ -56,9 +58,10 @@ public class ProcessExecutor {
long timeoutMinutes =
switch (key) {
case LIBRE_OFFICE -> 30;
case PDFTOHTML -> 20;
case OCR_MY_PDF -> 30;
case PYTHON_OPENCV -> 30;
case GHOSTSCRIPT -> 5;
case GHOSTSCRIPT -> 30;
case WEASYPRINT -> 30;
case INSTALL_APP -> 60;
case CALIBRE -> 30;
@@ -169,26 +172,30 @@ public class ProcessExecutor {
errorReaderThread.join();
outputReaderThread.join();
if (!liveUpdates) {
if (outputLines.size() > 0) {
String outputMessage = String.join("\n", outputLines);
messages += outputMessage;
if (outputLines.size() > 0) {
String outputMessage = String.join("\n", outputLines);
messages += outputMessage;
if (!liveUpdates) {
logger.info("Command output:\n" + outputMessage);
}
}
if (errorLines.size() > 0) {
String errorMessage = String.join("\n", errorLines);
messages += errorMessage;
if (errorLines.size() > 0) {
String errorMessage = String.join("\n", errorLines);
messages += errorMessage;
if (!liveUpdates) {
logger.warn("Command error output:\n" + errorMessage);
if (exitCode != 0) {
throw new IOException(
"Command process failed with exit code "
+ exitCode
+ ". Error message: "
+ errorMessage);
}
}
} else if (exitCode != 0) {
if (exitCode != 0) {
throw new IOException(
"Command process failed with exit code "
+ exitCode
+ ". Error message: "
+ errorMessage);
}
}
if (exitCode != 0) {
throw new IOException(
"Command process failed with exit code "
+ exitCode

View File

@@ -24,8 +24,8 @@ spring.devtools.livereload.enabled=true
spring.thymeleaf.encoding=UTF-8
server.connection-timeout=${SYSTEM_CONNECTIONTIMEOUTMINUTES:5m}
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:300000}
server.connection-timeout=${SYSTEM_CONNECTIONTIMEOUTMINUTES:20m}
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
spring.resources.static-locations=file:customFiles/static/
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/

View File

@@ -13,17 +13,17 @@ processTimeWarning=تحذير: يمكن أن تستغرق هذه العملية
pageOrderPrompt=ترتيب الصفحات (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
goToPage=اذهب
true=\u0635\u062D\u064A\u062D
false=\u062E\u0637\u0623
unknown=\u063A\u064A\u0631 \u0645\u0639\u0631\u0648\u0641
save=\u062D\u0641\u0638
true=صحيح
false=خطأ
unknown=غير معروف
save=حفظ
saveToBrowser=Save to Browser
close=\u0625\u063A\u0644\u0627\u0642
close=إغلاق
filesSelected=الملفات المحددة
noFavourites=لم تتم إضافة أي مفضلات
downloadComplete=Download Complete
bored=الانتظار بالملل؟
alphabet=\u0627\u0644\u0623\u0628\u062C\u062F\u064A\u0629
alphabet=الأبجدية
downloadPdf=تنزيل PDF
text=نص
font=الخط
@@ -54,9 +54,25 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,28 +105,36 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=تحويل
navbar.security=الأمان
navbar.other=أخرى
navbar.favorite=Favorites
navbar.darkmode=الوضع الداكن
navbar.pageOps=عمليات الصفحة
navbar.settings=\u0625\u0639\u062F\u0627\u062F\u0627\u062A
navbar.language=Languages
navbar.settings=إعدادات
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=\u0627\u0644\u0625\u0639\u062F\u0627\u062F\u0627\u062A
settings.update=\u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0645\u062A\u0627\u062D
settings.appVersion=\u0625\u0635\u062F\u0627\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642:
settings.downloadOption.title=\u062A\u062D\u062F\u064A\u062F \u062E\u064A\u0627\u0631 \u0627\u0644\u062A\u0646\u0632\u064A\u0644 (\u0644\u0644\u062A\u0646\u0632\u064A\u0644\u0627\u062A \u0630\u0627\u062A \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0648\u0627\u062D\u062F \u063A\u064A\u0631 \u0627\u0644\u0645\u0636\u063A\u0648\u0637):
settings.downloadOption.1=\u0641\u062A\u062D \u0641\u064A \u0646\u0641\u0633 \u0627\u0644\u0646\u0627\u0641\u0630\u0629
settings.downloadOption.2=\u0641\u062A\u062D \u0641\u064A \u0646\u0627\u0641\u0630\u0629 \u062C\u062F\u064A\u062F\u0629
settings.downloadOption.3=\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u0644\u0641
settings.zipThreshold=\u0645\u0644\u0641\u0627\u062A \u0645\u0636\u063A\u0648\u0637\u0629 \u0639\u0646\u062F \u062A\u062C\u0627\u0648\u0632 \u0639\u062F\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0646\u0632\u064A\u0644\u0647\u0627
settings.title=الإعدادات
settings.update=التحديث متاح
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=إصدار التطبيق:
settings.downloadOption.title=تحديد خيار التنزيل (للتنزيلات ذات الملف الواحد غير المضغوط):
settings.downloadOption.1=فتح في نفس النافذة
settings.downloadOption.2=فتح في نافذة جديدة
settings.downloadOption.3=تنزيل الملف
settings.zipThreshold=ملفات مضغوطة عند تجاوز عدد الملفات التي تم تنزيلها
settings.signOut=Sign Out
settings.accountSettings=Account Settings
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Change Credentials
changeCreds.header=Update Your Account Details
@@ -147,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin
adminUserSettings.user=User
adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles
adminUserSettings.role=Role
adminUserSettings.actions=Actions
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=تغيير دور المستخدم
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -230,25 +258,25 @@ home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف.
compressPdfs.tags=squish,small,tiny
home.changeMetadata.title=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
home.changeMetadata.desc=\u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF
home.changeMetadata.title=تغيير البيانات الوصفية
home.changeMetadata.desc=تغيير / إزالة / إضافة بيانات أولية من مستند PDF
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
home.fileToPDF.title=\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 \u0625\u0644\u0649 PDF
home.fileToPDF.desc=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u062A\u0642\u0631\u064A\u0628\u0627 \u0625\u0644\u0649 PDF (DOCX \u0648PNG \u0648XLS \u0648PPT \u0648TXT \u0648\u0627\u0644\u0645\u0632\u064A\u062F)
home.fileToPDF.title=تحويل الملف إلى PDF
home.fileToPDF.desc=تحويل أي ملف تقريبا إلى PDF (DOCX وPNG وXLS وPPT وTXT والمزيد)
fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint
home.ocr.title=\u062A\u0634\u063A\u064A\u0644 OCR \u0639\u0644\u0649 PDF \u0648 / \u0623\u0648 \u0645\u0633\u062D \u0636\u0648\u0626\u064A
home.ocr.desc=\u064A\u0642\u0648\u0645 \u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0628\u0645\u0633\u062D \u0648\u0627\u0643\u062A\u0634\u0627\u0641 \u0627\u0644\u0646\u0635 \u0645\u0646 \u0627\u0644\u0635\u0648\u0631 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF \u0648\u064A\u0639\u064A\u062F \u0625\u0636\u0627\u0641\u062A\u0647 \u0643\u0646\u0635
home.ocr.title=تشغيل OCR على PDF و / أو مسح ضوئي
home.ocr.desc=يقوم برنامج التنظيف بمسح واكتشاف النص من الصور داخل ملف PDF ويعيد إضافته كنص
ocr.tags=recognition,text,image,scan,read,identify,detection,editable
home.extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
home.extractImages.desc=\u064A\u0633\u062A\u062E\u0631\u062C \u062C\u0645\u064A\u0639 \u0627\u0644\u0635\u0648\u0631 \u0645\u0646 \u0645\u0644\u0641 PDF \u0648\u064A\u062D\u0641\u0638\u0647\u0627 \u0641\u064A \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0628\u0631\u064A\u062F\u064A
home.extractImages.title=استخراج الصور
home.extractImages.desc=يستخرج جميع الصور من ملف PDF ويحفظها في الرمز البريدي
extractImages.tags=picture,photo,save,archive,zip,capture,grab
home.pdfToPDFA.title=\u062A\u062D\u0648\u064A\u0644 \u0645\u0644\u0641\u0627\u062A PDF \u0625\u0644\u0649 PDF / A
home.pdfToPDFA.desc=\u062A\u062D\u0648\u064A\u0644 PDF \u0625\u0644\u0649 PDF / A \u0644\u0644\u062A\u062E\u0632\u064A\u0646 \u0637\u0648\u064A\u0644 \u0627\u0644\u0645\u062F\u0649
home.pdfToPDFA.title=تحويل ملفات PDF إلى PDF / A
home.pdfToPDFA.desc=تحويل PDF إلى PDF / A للتخزين طويل المدى
pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation
home.PDFToWord.title=تحويل PDF إلى Word
@@ -422,6 +450,8 @@ login.rememberme=Remember me
login.invalid=Invalid username or password.
login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
login.oauth2AutoCreateDisabled=تم تعطيل مستخدم الإنشاء التلقائي لـ OAuth2
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Convert To Single Page
pageExtracter.title=Extract Pages
pageExtracter.header=Extract Pages
pageExtracter.submit=Extract
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -673,6 +704,7 @@ repair.submit=الإصلاح
#flatten
flatten.title=تسطيح
flatten.header=تسوية ملفات PDF
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=تسطيح
@@ -690,38 +722,39 @@ ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة وا
#OCR
ocr.title=\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 / \u062A\u0646\u0638\u064A\u0641 \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A
ocr.header=\u0645\u0633\u062D \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A / \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 (\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641)
ocr.selectText.1=\u062D\u062F\u062F \u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u062A\u064A \u0633\u064A\u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF (\u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u0645\u062F\u0631\u062C\u0629 \u0647\u064A \u062A\u0644\u0643 \u0627\u0644\u062A\u064A \u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062D\u0627\u0644\u064A\u064B\u0627):
ocr.selectText.2=\u0625\u0646\u062A\u0627\u062C \u0645\u0644\u0641 \u0646\u0635\u064A \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 OCR \u0628\u062C\u0627\u0646\u0628 \u0645\u0644\u0641 PDF \u0627\u0644\u0630\u064A \u062A\u0645 \u0625\u0639\u062F\u0627\u062F\u0647 \u0628\u0648\u0627\u0633\u0637\u0629 OCR
ocr.selectText.3=\u062A\u0645 \u0645\u0633\u062D \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u0635\u062D\u064A\u062D\u0629 \u0636\u0648\u0626\u064A\u064B\u0627 \u0628\u0632\u0627\u0648\u064A\u0629 \u0645\u0646\u062D\u0631\u0641\u0629 \u0639\u0646 \u0637\u0631\u064A\u0642 \u062A\u062F\u0648\u064A\u0631\u0647\u0627 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0641\u064A \u0645\u0643\u0627\u0646\u0647\u0627
ocr.selectText.4=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629. (\u0644\u0627 \u064A\u0648\u062C\u062F \u062A\u063A\u064A\u064A\u0631 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C)
ocr.selectText.5=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u060C \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629 \u060C \u0648\u064A\u062D\u0627\u0641\u0638 \u0639\u0644\u0649 \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C.
ocr.selectText.6=\u064A\u062A\u062C\u0627\u0647\u0644 \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 \u062A\u0641\u0627\u0639\u0644\u064A \u060C \u0641\u0642\u0637 \u0635\u0641\u062D\u0627\u062A OCRs \u0627\u0644\u062A\u064A \u0647\u064A \u0635\u0648\u0631
ocr.selectText.7=\u0641\u0631\u0636 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u060C \u0633\u064A\u0624\u062F\u064A \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u0639\u0644\u0649 \u0643\u0644 \u0635\u0641\u062D\u0629 \u0625\u0644\u0649 \u0625\u0632\u0627\u0644\u0629 \u062C\u0645\u064A\u0639 \u0639\u0646\u0627\u0635\u0631 \u0627\u0644\u0646\u0635 \u0627\u0644\u0623\u0635\u0644\u064A
ocr.selectText.8=\u0639\u0627\u062F\u064A (\u062E\u0637\u0623 \u0625\u0630\u0627 \u0643\u0627\u0646 PDF \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635)
ocr.selectText.9=\u0625\u0639\u062F\u0627\u062F\u0627\u062A \u0625\u0636\u0627\u0641\u064A\u0629
ocr.selectText.10=\u0648\u0636\u0639 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641
ocr.title=التعرف الضوئي على الحروف / تنظيف المسح الضوئي
ocr.header=مسح المسح الضوئي / التعرف الضوئي على الحروف (التعرف الضوئي على الحروف)
ocr.selectText.1=حدد اللغات التي سيتم اكتشافها داخل ملف PDF (اللغات المدرجة هي تلك التي تم اكتشافها حاليًا):
ocr.selectText.2=إنتاج ملف نصي يحتوي على نص OCR بجانب ملف PDF الذي تم إعداده بواسطة OCR
ocr.selectText.3=تم مسح الصفحات الصحيحة ضوئيًا بزاوية منحرفة عن طريق تدويرها مرة أخرى في مكانها
ocr.selectText.4=صفحة نظيفة لذلك من غير المحتمل أن يجد OCR نصًا في ضوضاء الخلفية. (لا يوجد تغيير في الإخراج)
ocr.selectText.5=صفحة نظيفة ، لذلك من غير المحتمل أن يجد OCR نصًا في ضوضاء الخلفية ، ويحافظ على التنظيف في الإخراج.
ocr.selectText.6=يتجاهل الصفحات التي تحتوي على نص تفاعلي ، فقط صفحات OCRs التي هي صور
ocr.selectText.7=فرض التعرف الضوئي على الحروف ، سيؤدي التعرف الضوئي على الحروف على كل صفحة إلى إزالة جميع عناصر النص الأصلي
ocr.selectText.8=عادي (خطأ إذا كان PDF يحتوي على نص)
ocr.selectText.9=إعدادات إضافية
ocr.selectText.10=وضع التعرف الضوئي على الحروف
ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور ، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل)
ocr.selectText.12=نوع العرض (متقدم)
ocr.help=\u064A\u0631\u062C\u0649 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0648\u062B\u0627\u0626\u0642 \u062D\u0648\u0644 \u0643\u064A\u0641\u064A\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0647\u0630\u0627 \u0644\u0644\u063A\u0627\u062A \u0623\u062E\u0631\u0649 \u0648 / \u0623\u0648 \u0627\u0644\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0644\u064A\u0633 \u0641\u064A \u0639\u0627\u0645\u0644 \u0627\u0644\u0625\u0631\u0633\u0627\u0621
ocr.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0648 Tesseract \u0644 OCR.
ocr.submit=\u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR
ocr.help=يرجى قراءة هذه الوثائق حول كيفية استخدام هذا للغات أخرى و / أو الاستخدام ليس في عامل الإرساء
ocr.credit=تستخدم هذه الخدمة OCRmyPDF و Tesseract ل OCR.
ocr.submit=معالجة PDF باستخدام OCR
#extractImages
extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
extractImages.header=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
extractImages.selectText=\u062D\u062F\u062F \u062A\u0646\u0633\u064A\u0642 \u0627\u0644\u0635\u0648\u0631\u0629 \u0644\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0635\u0648\u0631 \u0627\u0644\u0645\u0633\u062A\u062E\u0631\u062C\u0629 \u0625\u0644\u0649
extractImages.submit=\u0627\u0633\u062A\u062E\u0631\u0627\u062C
extractImages.title=استخراج الصور
extractImages.header=استخراج الصور
extractImages.selectText=حدد تنسيق الصورة لتحويل الصور المستخرجة إلى
extractImages.submit=استخراج
#File to PDF
fileToPDF.title=\u0645\u0644\u0641 \u0625\u0644\u0649 PDF
fileToPDF.header=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u0625\u0644\u0649 PDF
fileToPDF.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 \u0644\u064A\u0628\u0631 \u0623\u0648\u0641\u064A\u0633 \u0648\u0623\u0648\u0646\u0648\u0643\u0648\u0646\u0641 \u0644\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641\u0627\u062A.
fileToPDF.supportedFileTypes=\u064A\u062C\u0628 \u0623\u0646 \u062A\u062A\u0636\u0645\u0646 \u0623\u0646\u0648\u0627\u0639 \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u0645\u062F\u0639\u0648\u0645\u0629 \u0645\u0627 \u064A\u0644\u064A \u0648\u0644\u0643\u0646 \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0642\u0627\u0626\u0645\u0629 \u0645\u062D\u062F\u062B\u0629 \u0643\u0627\u0645\u0644\u0629 \u0628\u0627\u0644\u062A\u0646\u0633\u064A\u0642\u0627\u062A \u0627\u0644\u0645\u062F\u0639\u0648\u0645\u0629 \u060C \u064A\u0631\u062C\u0649 \u0627\u0644\u0631\u062C\u0648\u0639 \u0625\u0644\u0649 \u0648\u062B\u0627\u0626\u0642 LibreOffice
fileToPDF.submit=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 PDF
fileToPDF.title=ملف إلى PDF
fileToPDF.header=تحويل أي ملف إلى PDF
fileToPDF.credit=تستخدم هذه الخدمة ليبر أوفيس وأونوكونف لتحويل الملفات.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=يجب أن تتضمن أنواع الملفات المدعومة ما يلي ولكن للحصول على قائمة محدثة كاملة بالتنسيقات المدعومة ، يرجى الرجوع إلى وثائق LibreOffice
fileToPDF.submit=تحويل إلى PDF
#compress
@@ -756,11 +789,23 @@ merge.submit=دمج
pdfOrganiser.title=منظم الصفحة
pdfOrganiser.header=منظم صفحات PDF
pdfOrganiser.submit=إعادة ترتيب الصفحات
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=أداة متعددة PDF
multiTool.header=أداة متعددة PDF
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=View PDF
@@ -771,6 +816,7 @@ pageRemover.title=مزيل الصفحة
pageRemover.header=مزيل صفحة PDF
pageRemover.pagesToDelete=الصفحات المراد حذفها (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
pageRemover.submit=حذف الصفحات
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=حدد زاوية الدوران (بمضاعفات 90 درج
rotate.submit=استدارة
#merge
#split-pdfs
split.title=انقسام PDF
split.header=تقسيم PDF
split.desc.1=الأرقام التي تحددها هي رقم الصفحة التي تريد تقسيمها
split.desc.2=على هذا النحو ، سيؤدي تحديد 1،3،7-8 إلى تقسيم مستند من 10 صفحات إلى 6 PDFS منفصلة مع:
split.desc.2=على هذا النحو ، سيؤدي تحديد 1،3،7-9 إلى تقسيم مستند من 10 صفحات إلى 6 PDFS منفصلة مع:
split.desc.3=المستند رقم 1: الصفحة 1
split.desc.4=المستند رقم 2: الصفحتان 2 و 3
split.desc.5=المستند رقم 3: الصفحة 4 و 5 و 6
split.desc.6=المستند رقم 4: الصفحة 7
split.desc.7=المستند رقم 5: الصفحة 8
split.desc.8=المستند رقم 6: الصفحتان 9 و 10
split.desc.5=المستند رقم 3: الصفحة 4 و 5 و 6 و 7
split.desc.6=المستند رقم 4: الصفحة 8
split.desc.7=المستند رقم 5: الصفحة 9
split.desc.8=المستند رقم 6: الصفحة 10
split.splitPages=أدخل الصفحات المراد تقسيمها:
split.submit=Split
@@ -803,23 +849,23 @@ imageToPDF.selectLabel=Image Fit Options
imageToPDF.fillPage=Fill Page
imageToPDF.fitDocumentToImage=Fit Page to Image
imageToPDF.maintainAspectRatio=Maintain Aspect Ratios
imageToPDF.selectText.2=\u062F\u0648\u0631\u0627\u0646 PDF \u062A\u0644\u0642\u0627\u0626\u064A\u064B\u0627
imageToPDF.selectText.3=\u0627\u0644\u0645\u0646\u0637\u0642 \u0627\u0644\u0645\u062A\u0639\u062F\u062F \u0644\u0644\u0645\u0644\u0641\u0627\u062A (\u0645\u0641\u0639\u0651\u0644 \u0641\u0642\u0637 \u0625\u0630\u0627 \u0643\u0646\u062A \u062A\u0639\u0645\u0644 \u0645\u0639 \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629)
imageToPDF.selectText.4=\u062F\u0645\u062C \u0641\u064A \u0645\u0644\u0641 PDF \u0648\u0627\u062D\u062F
imageToPDF.selectText.5=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 \u0645\u0644\u0641\u0627\u062A PDF \u0645\u0646\u0641\u0635\u0644\u0629
imageToPDF.selectText.2=دوران PDF تلقائيًا
imageToPDF.selectText.3=المنطق المتعدد للملفات (مفعّل فقط إذا كنت تعمل مع صور متعددة)
imageToPDF.selectText.4=دمج في ملف PDF واحد
imageToPDF.selectText.5=تحويل إلى ملفات PDF منفصلة
#pdfToImage
pdfToImage.title=تحويل PDF إلى صورة
pdfToImage.header=تحويل PDF إلى صورة
pdfToImage.selectText=تنسيق الصورة
pdfToImage.singleOrMultiple=\u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629
pdfToImage.single=\u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629
pdfToImage.multi=\u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629
pdfToImage.colorType=\u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646
pdfToImage.color=\u0627\u0644\u0644\u0648\u0646
pdfToImage.grey=\u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A
pdfToImage.blackwhite=\u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!)
pdfToImage.singleOrMultiple=نوع نتيجة الصورة
pdfToImage.single=صورة واحدة كبيرة
pdfToImage.multi=صور متعددة
pdfToImage.colorType=نوع اللون
pdfToImage.color=اللون
pdfToImage.grey=تدرج الرمادي
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
pdfToImage.submit=تحول
@@ -854,10 +900,12 @@ watermark.selectText.3=حجم الخط:
watermark.selectText.4=دوران (0-360):
watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا):
watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا):
watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100\u066A):
watermark.selectText.7=التعتيم (0٪ - 100٪):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.submit=إضافة علامة مائية
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -886,29 +934,31 @@ removePassword.submit=إزالة
#changeMetadata
changeMetadata.title=\u0627\u0644\u0639\u0646\u0648\u0627\u0646:
changeMetadata.header=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
changeMetadata.selectText.1=\u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627
changeMetadata.selectText.2=\u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629
changeMetadata.selectText.3=\u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629:
changeMetadata.author=\u0627\u0644\u0645\u0624\u0644\u0641:
changeMetadata.creationDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss):
changeMetadata.creator=\u0627\u0644\u0645\u0646\u0634\u0626:
changeMetadata.keywords=\u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629:
changeMetadata.modDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss):
changeMetadata.producer=\u0627\u0644\u0645\u0646\u062A\u062C:
changeMetadata.subject=\u0627\u0644\u0645\u0648\u0636\u0648\u0639:
changeMetadata.trapped=\u0645\u062D\u0627\u0635\u0631:
changeMetadata.selectText.4=\u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649:
changeMetadata.selectText.5=\u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635
changeMetadata.submit=\u062A\u063A\u064A\u064A\u0631
changeMetadata.title=العنوان:
changeMetadata.header=تغيير البيانات الوصفية
changeMetadata.selectText.1=يرجى تعديل المتغيرات التي ترغب في تغييرها
changeMetadata.selectText.2=حذف كل البيانات الأولية
changeMetadata.selectText.3=إظهار البيانات الأولية المخصصة:
changeMetadata.author=المؤلف:
changeMetadata.creationDate=تاريخ الإنشاء (yyyy / MM / dd HH: mm: ss):
changeMetadata.creator=المنشئ:
changeMetadata.keywords=الكلمات الرئيسية:
changeMetadata.modDate=تاريخ التعديل (yyyy / MM / dd HH: mm: ss):
changeMetadata.producer=المنتج:
changeMetadata.subject=الموضوع:
changeMetadata.trapped=محاصر:
changeMetadata.selectText.4=بيانات وصفية أخرى:
changeMetadata.selectText.5=إضافة إدخال بيانات أولية مخصص
changeMetadata.submit=تغيير
#pdfToPDFA
pdfToPDFA.title=PDF \u0625\u0644\u0649 PDF / A
pdfToPDFA.header=PDF \u0625\u0644\u0649 PDF / A
pdfToPDFA.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0644\u062A\u062D\u0648\u064A\u0644 PDF / A.
pdfToPDFA.submit=\u062A\u062D\u0648\u064A\u0644
pdfToPDFA.title=PDF إلى PDF / A
pdfToPDFA.header=PDF إلى PDF / A
pdfToPDFA.credit=تستخدم هذه الخدمة OCRmyPDF لتحويل PDF / A.
pdfToPDFA.submit=تحويل
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=تحويل
#PDFToHTML
PDFToHTML.title=PDF إلى HTML
PDFToHTML.header=PDF إلى HTML
PDFToHTML.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
PDFToHTML.credit=تستخدم هذه الخدمة pdftohtml لتحويل الملفات.
PDFToHTML.submit=تحويل
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
split-by-sections.submit=Split PDF
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

View File

@@ -11,17 +11,17 @@ imgPrompt=Изберете изображение(я)
genericSubmit=Подайте
processTimeWarning=Предупреждение: Този процес може да отнеме до минута в зависимост от размера на файла
pageOrderPrompt=Персонализиран ред на страниците (Въведете разделен със запетаи списък с номера на страници или функции като 2n+1):
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
pageSelectionPrompt=Персонализиран избор на страница (Въведете списък с номера на страници 1,5,6, разделени със запетая, или функции като 2n+1) :
goToPage=Давай
true=Вярно
false=Невярно
unknown=Непознат
save=Съхранете
saveToBrowser=Save to Browser
saveToBrowser=Съхраняване в браузъра
close=Затворете
filesSelected=избрани файлове
noFavourites=Няма добавени любими
downloadComplete=Download Complete
downloadComplete=Свалянето завършено
bored=Отекчени сте да чакате?
alphabet=Азбука
downloadPdf=Изтеглете PDF
@@ -45,41 +45,59 @@ red=Червено
green=Зелено
blue=Синьо
custom=Персонализиране...
WorkInProgess=Work in progress, May not work or be buggy, Please report any problems!
poweredBy=Powered by
yes=Yes
no=No
WorkInProgess=Работата е в ход, може да не работи или да има грешки, моля, докладвайте за проблеми!
poweredBy=Задвижван чрез
yes=Да
no=Не
changedCredsMessage=Идентификационните данни са променени!
notAuthenticatedMessage=Потребителят не е автентикиран.
userNotFoundMessage=Потребителят не е намерен
incorrectPasswordMessage=Текущата парола е неправилна.
usernameExistsMessage=Новият потребител вече съществува.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
invalidUsernameMessage=Невалидно потребителско име, потребителското име може да съдържа само букви, цифри и следните специални знаци @._+- или трябва да е валиден имейл адрес.
deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител.
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Грешка
oops=Опаа!
help=Помощ
goHomepage=Отидете на началната страница
joinDiscord=Присъединете се към нашия Discord сървър
seeDockerHub=Погледнете Docker Hub
visitGithub=Посетете Github Repository
donate=Направете дарение
color=Цвят
sponsor=Спонсор
info=Info
###############
# Pipeline #
###############
pipeline.header=Pipeline Menu (Beta)
pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.header=Pipeline Меню (Бета)
pipeline.uploadButton=Качване на персонализиран
pipeline.configureButton=Настройка
pipeline.defaultOption=Персонализиран
pipeline.submitButton=Подайте
pipeline.help=Pipeline Помощ
pipeline.scanHelp=Помощ за сканиране на папки
######################
# Pipeline Options #
######################
pipelineOptions.header=Pipeline Configuration
pipelineOptions.pipelineNameLabel=Pipeline Name
pipelineOptions.saveSettings=Save Operation Settings
pipelineOptions.pipelineNamePrompt=Enter pipeline name here
pipelineOptions.selectOperation=Select Operation
pipelineOptions.addOperationButton=Add operation
pipelineOptions.header=Pipeline Конфигурация
pipelineOptions.pipelineNameLabel=Pipeline име
pipelineOptions.saveSettings=Запазете настройките за работа
pipelineOptions.pipelineNamePrompt=Въведете името на pipeline тук
pipelineOptions.selectOperation=Избор на операция
pipelineOptions.addOperationButton=Добавяне на операция
pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
pipelineOptions.saveButton=Изтегли
pipelineOptions.validateButton=Валидирай
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=Преобразуване
navbar.security=Сигурност
navbar.other=Разни
navbar.favorite=Favorites
navbar.darkmode=Тъмна тема
navbar.pageOps=Операции със страници
navbar.language=Languages
navbar.settings=Настройки
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Настройки
settings.update=Налична актуализация
settings.updateAvailable={0} е текущата инсталирана версия. Налична е нова версия ({1}).
settings.appVersion=Версия на приложението:
settings.downloadOption.title=Изберете опция за изтегляне (за изтегляния на един файл без да е архивиран):
settings.downloadOption.1=Отваряне в същия прозорец
@@ -107,12 +132,13 @@ settings.downloadOption.3=Изтегли файл
settings.zipThreshold=Архивирайте файловете, когато броят на изтеглените файлове надвишава
settings.signOut=Изход
settings.accountSettings=Настройки на акаунта
settings.bored.help=Активира игра с великденски яйца
settings.cacheInputs.name=Запазете въведените формуляри
settings.cacheInputs.help=Активирайте за съхраняване на предишни използвани въведени данни за бъдещи изпълнения
changeCreds.title=Промяна на идентификационните данни
changeCreds.header=Актуализирайте данните за акаунта си
changeCreds.changePassword=You are using default login credentials. Please enter a new password
changeCreds.changePassword=Използвате идентификационни данни за вход по подразбиране. Моля, въведете нова парола
changeCreds.newUsername=Ново потребителско име
changeCreds.oldPassword=Текуща парола
changeCreds.newPassword=Нова парола
@@ -138,8 +164,8 @@ account.syncTitle=Синхронизиране на настройките на
account.settingsCompare=Сравняване на настройките:
account.property=Свойство
account.webBrowserSettings=Уеб-браузър настройки
account.syncToBrowser=Синхронизиране на акаунт -> Бразър
account.syncToAccount=Синхронизиране на акаунт <- Бразър
account.syncToBrowser=Синхронизиране на акаунт -> Браузър
account.syncToAccount=Синхронизиране на акаунт <- Браузър
adminUserSettings.title=Настройки за потребителски контрол
@@ -147,28 +173,30 @@ adminUserSettings.header=Настройки за администраторск
adminUserSettings.admin=Администратор
adminUserSettings.user=Потребител
adminUserSettings.addUser=Добавяне на нов потребител
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес.
adminUserSettings.roles=Роли
adminUserSettings.role=Роля
adminUserSettings.actions=Действия
adminUserSettings.apiUser=Ограничен API потребител
adminUserSettings.extraApiUser=Additional Limited API User
adminUserSettings.extraApiUser=Допълнителен ограничен API потребител
adminUserSettings.webOnlyUser=Само за уеб-потребител
adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.demoUser=Демо потребител (без персонализирани настройки)
adminUserSettings.internalApiUser=Вътрешен API потребител
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
adminUserSettings.submit=Съхранете потребителя
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
#############
home.desc=Вашето локално хоствано обслужване на едно място за всички ваши PDF нужди.
home.searchBar=Search for features...
home.searchBar=Търсене на функции...
home.viewPdf.title=View PDF
home.viewPdf.desc=View, annotate, add text or images
viewPdf.tags=view,read,annotate,text,image
home.viewPdf.title=Преглед на PDF
home.viewPdf.desc=Преглеждайте, коментирайте, добавяйте текст или изображения
viewPdf.tags=преглед,четене,анотиране,текст,изображение
home.multiTool.title=PDF Мулти инструмент
home.multiTool.desc=Обединяване, завъртане, пренареждане и премахване на страници
@@ -239,7 +267,7 @@ home.fileToPDF.desc=Преобразуване почти всеки файл к
fileToPDF.tags=трансформация,формат,документ,изображение,слайд,текст,преобразуване,офис,документи,word,excel,powerpoint
home.ocr.title=OCR / Почистващи сканирания
home.ocr.desc=Cleanup сканира и открива текст от изображения към PDF и го добавя отново като текст.
home.ocr.desc=Почистване, сканира и открива текст от изображения към PDF и го добавя отново като текст.
ocr.tags=разпознаване,текст,изображение,сканиране,четене,идентифициране,откриване,редактиране
@@ -292,9 +320,9 @@ home.removeBlanks.title=Премахване на празни страници
home.removeBlanks.desc=Открива и премахва празни страници от документ
removeBlanks.tags=почистване,рационализиране,без съдържание,организиране
home.removeAnnotations.title=Remove Annotations
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
removeAnnotations.tags=comments,highlight,notes,markup,remove
home.removeAnnotations.title=Премахване на анотации
home.removeAnnotations.desc=Премахва всички коментари/анотации от PDF
removeAnnotations.tags=коментари, маркиране, бележки, маркиране, премахване
home.compare.title=Сравнете
home.compare.desc=Сравнява и показва разликите между 2 PDF документа
@@ -336,7 +364,7 @@ home.autoSplitPDF.title=Автоматично разделяне на стра
home.autoSplitPDF.desc=Автоматично разделяне на сканиран PDF файл с QR код за разделяне на физически сканирани страници
autoSplitPDF.tags=QR-базиран,отделен,сканиране-сегмент,организиране
home.sanitizePdf.title=Дезинфекцирай
home.sanitizePdf.title=Дезинфенкцирам
home.sanitizePdf.desc=Премахване на скриптове и други елементи от PDF файлове
sanitizePdf.tags=чисти,сигурни,безопасни,премахване-заплахи
@@ -378,35 +406,35 @@ home.autoRedact.desc=Автоматично редактира (зачерняв
autoRedact.tags=Редактиране,Скриване,затъмняване,черен,маркер,скрит
home.tableExtraxt.title=PDF to CSV
home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV
tableExtraxt.tags=CSV,Table Extraction,extract,convert
home.tableExtraxt.desc=Извлича таблици от PDF, като ги конвертира в CSV
tableExtraxt.tags=CSV,извличане на таблица,извличане,конвертиране
home.autoSizeSplitPDF.title=Auto Split by Size/Count
home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count
autoSizeSplitPDF.tags=pdf,split,document,organization
home.autoSizeSplitPDF.title=Автоматично разделяне по размер/брой
home.autoSizeSplitPDF.desc=Разделете един PDF на множество документи въз основа на размер, брой страници или брой документи
autoSizeSplitPDF.tags=pdf,разделяне,документ,организация
home.overlay-pdfs.title=Overlay PDFs
home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF
overlay-pdfs.tags=Overlay
home.overlay-pdfs.title=Наслагване PDF
home.overlay-pdfs.desc=Наслагва PDF файлове върху друг PDF
overlay-pdfs.tags=Наслагване
home.split-by-sections.title=Split PDF by Sections
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
split-by-sections.tags=Section Split, Divide, Customize
home.split-by-sections.title=Разделяне на PDF по секции
home.split-by-sections.desc=Разделете всяка страница от PDF на по-малки хоризонтални и вертикални секции
split-by-sections.tags=Разделяне на секция,Разделяне,Персонализиране
home.AddStampRequest.title=Add Stamp to PDF
home.AddStampRequest.desc=Add text or add image stamps at set locations
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
home.AddStampRequest.title=Добавяне на печат към PDF
home.AddStampRequest.desc=Добавете текст или добавете печати с изображения на определени места
AddStampRequest.tags=Печат,добавяне на изображение,централно изображение,воден знак,PDF,вграждане,персонализиране
home.PDFToBook.title=PDF to Book
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.PDFToBook.title=PDF към книга
home.PDFToBook.desc=Преобразува PDF във формати на книги/комикси с помощта на calibre
PDFToBook.tags=Книга,комикс,calibre,конвертиране,манга,Amazon,Kindle
home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.BookToPDF.title=Книга към PDF
home.BookToPDF.desc=Преобразува формати на книги/комикси в PDF с помощта на calibre
BookToPDF.tags=Книга,комикс,calibre,конвертиране,манга,Amazon,Kindle
###########################
@@ -422,6 +450,8 @@ login.rememberme=Запомни ме
login.invalid=Невалидно потребителско име или парола.
login.locked=Вашият акаунт е заключен.
login.signinTitle=Моля впишете се
login.ssoSignIn=Влизане чрез еднократно влизане
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
#auto-redact
@@ -451,9 +481,10 @@ pdfToSinglePage.submit=Преобразуване към единична стр
#pageExtracter
pageExtracter.title=Extract Pages
pageExtracter.header=Extract Pages
pageExtracter.submit=Extract
pageExtracter.title=Извличане на страници
pageExtracter.header=Извличане на страници
pageExtracter.submit=Извличане
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -482,40 +513,40 @@ URLToPDF.credit=Използва WeasyPrint
#html-to-pdf
HTMLToPDF.title=HTML към PDF
HTMLToPDF.header=HTML към PDF
HTMLToPDF.help=Приема HTML файлове и ZIP файлове, съдържащи html/css/изображения и т.н
HTMLToPDF.help=Приемане на HTML файлове и ZIP файлове, съдържащи html/css/изображения и т.н.
HTMLToPDF.submit=Преобразуване
HTMLToPDF.credit=Използва WeasyPrint
HTMLToPDF.zoom=Zoom level for displaying the website.
HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default)
HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default)
HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default)
HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default)
HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default)
HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default)
HTMLToPDF.printBackground=Render the background of websites.
HTMLToPDF.defaultHeader=Enable Default Header (Name and page number)
HTMLToPDF.cssMediaType=Change the CSS media type of the page.
HTMLToPDF.none=None
HTMLToPDF.print=Print
HTMLToPDF.screen=Screen
HTMLToPDF.zoom=Ниво на мащабиране за показване на уебсайта.
HTMLToPDF.pageWidth=Ширина на страницата в сантиметри. (Празно по подразбиране)
HTMLToPDF.pageHeight=Височина на страницата в сантиметри. (Празно по подразбиране)
HTMLToPDF.marginTop=Горно поле на страницата в милиметри. (Празно по подразбиране)
HTMLToPDF.marginBottom=Долно поле на страницата в милиметри. (Празно по подразбиране)
HTMLToPDF.marginLeft=Ляво поле на страницата в милиметри. (Празно по подразбиране)
HTMLToPDF.marginRight=Дясно поле на страницата в милиметри. (Празно по подразбиране)
HTMLToPDF.printBackground=Изобразете фона на уебсайтове.
HTMLToPDF.defaultHeader=Активиране на горния колонтитул по подразбиране (име и номер на страница)
HTMLToPDF.cssMediaType=Променете CSS медийния тип на страницата.
HTMLToPDF.none=Няма
HTMLToPDF.print=Печат
HTMLToPDF.screen=Екран
#AddStampRequest
AddStampRequest.header=Stamp PDF
AddStampRequest.title=Stamp PDF
AddStampRequest.stampType=Stamp Type
AddStampRequest.stampText=Stamp Text
AddStampRequest.stampImage=Stamp Image
AddStampRequest.alphabet=Alphabet
AddStampRequest.fontSize=Font/Image Size
AddStampRequest.rotation=Rotation
AddStampRequest.opacity=Opacity
AddStampRequest.position=Position
AddStampRequest.overrideX=Override X Coordinate
AddStampRequest.overrideY=Override Y Coordinate
AddStampRequest.customMargin=Custom Margin
AddStampRequest.customColor=Custom Text Color
AddStampRequest.submit=Submit
AddStampRequest.header=Поставяне на печат на PDF
AddStampRequest.title=Поставяне на печат на PDF
AddStampRequest.stampType=Тип печат
AddStampRequest.stampText=Поставяне на текст
AddStampRequest.stampImage=Изображение с печат
AddStampRequest.alphabet=Азбука
AddStampRequest.fontSize=Размер на шрифта/изображението
AddStampRequest.rotation=Ротация
AddStampRequest.opacity=Непрозрачност
AddStampRequest.position=Позиция
AddStampRequest.overrideX=Замяна на X координата
AddStampRequest.overrideY=Замяна на Y координата
AddStampRequest.customMargin=Персонализиран марж
AddStampRequest.customColor=Персонализиран цвят на текста
AddStampRequest.submit=Изпращане
#sanitizePDF
@@ -604,11 +635,11 @@ scalePages.submit=Подайте
certSign.title=Подписване на сертификат
certSign.header=Подпишете PDF с вашия сертификат (В процес на работа)
certSign.selectPDF=Изберете PDF файл за подписване:
certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below.
certSign.jksNote=Забележка: Ако вашият тип сертификат не е в списъка по-долу, моля, конвертирайте го във файл на Java Keystore (.jks) с помощта на инструмента за команден ред keytool. След това изберете опцията за .jks файл по-долу.
certSign.selectKey=Изберете вашия файл с личен ключ (формат PKCS#8, може да бъде .pem или .der):
certSign.selectCert=Изберете вашия файл със сертификат (формат X.509, може да бъде .pem или .der):
certSign.selectP12=Изберете вашия PKCS#12 Keystore файл (.p12 или .pfx) (По избор, ако е предоставен, трябва да съдържа вашия личен ключ и сертификат):
certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore):
certSign.selectJKS=Изберете Вашия Java Keystore Файл (.jks или .keystore):
certSign.certType=Тип сертификат
certSign.password=Въведете вашата парола за Keystore за ключове или частен ключ (ако има):
certSign.showSig=Показване на подпис
@@ -629,9 +660,9 @@ removeBlanks.submit=Премахване на празни места
#removeAnnotations
removeAnnotations.title=Remove Annotations
removeAnnotations.header=Remove Annotations
removeAnnotations.submit=Remove
removeAnnotations.title=Премахване на анотации
removeAnnotations.header=Премахване на анотации
removeAnnotations.submit=Премахване
#compare
@@ -642,17 +673,17 @@ compare.document.2=Документ 2
compare.submit=Сравнявай
#BookToPDF
BookToPDF.title=Books and Comics to PDF
BookToPDF.header=Book to PDF
BookToPDF.credit=Uses Calibre
BookToPDF.submit=Convert
BookToPDF.title=Книги и комикси в PDF
BookToPDF.header=Книга в PDF
BookToPDF.credit=Използва Calibre
BookToPDF.submit=Конвертиране
#PDFToBook
PDFToBook.title=PDF to Book
PDFToBook.header=PDF to Book
PDFToBook.selectText.1=Format
PDFToBook.credit=Uses Calibre
PDFToBook.submit=Convert
PDFToBook.title=PDF към книга
PDFToBook.header=PDF към книга
PDFToBook.selectText.1=Формат
PDFToBook.credit=Използва Calibre
PDFToBook.submit=Конвертиране
#sign
sign.title=Подпишете
@@ -673,6 +704,7 @@ repair.submit=Поправи
#flatten
flatten.title=Изравнете
flatten.header=Изравнете PDF-и
flatten.flattenOnlyForms=Изравнете само форми
flatten.submit=Изравнете
@@ -720,6 +752,7 @@ extractImages.submit=Извличане
fileToPDF.title=Файл към PDF
fileToPDF.header=Конвертирайте всеки файл към PDF
fileToPDF.credit=Тази услуга използва LibreOffice и Unoconv за преобразуване на файлове.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Поддържаните типове файлове трябва да включват по-долу, но за пълен актуализиран списък на поддържаните формати, моля, вижте документацията на LibreOffice
fileToPDF.submit=Преобразуване към PDF
@@ -732,7 +765,7 @@ compress.selectText.1=Ръчен режим - От 1 до 4
compress.selectText.2=Ниво на оптимизация:
compress.selectText.3=4 (Ужасно за текстови изображения)
compress.selectText.4=Автоматичен режим - Автоматично настройва качеството, за да получи PDF точен размер
compress.selectText.5=Очакван PDF размер (напр. 25MB, 10.8MB, 25KB)
compress.selectText.5=Очакван PDF размер (напр. 25МБ, 10.8МБ, 25КБ)
compress.submit=Компресиране
@@ -756,21 +789,34 @@ merge.submit=Обединяване
pdfOrganiser.title=Организатор на страници
pdfOrganiser.header=Организатор на PDF страници
pdfOrganiser.submit=Пренареждане на страниците
pdfOrganiser.mode=Режим
pdfOrganiser.mode.1=Персонализиран ред на страниците
pdfOrganiser.mode.2=Обърнат ред
pdfOrganiser.mode.3=Двустранно сортиране
pdfOrganiser.mode.4=Сортиране на брошури
pdfOrganiser.mode.5=Сортиране на брошури със страничен шев
pdfOrganiser.mode.6=Четно-нечетно разделяне
pdfOrganiser.mode.7=Премахни първо
pdfOrganiser.mode.8=Премахване на последния
pdfOrganiser.mode.9=Премахване на първия и последния
pdfOrganiser.placeholder=(напр. 1,3,2 или 4-8,2,10-12 или 2n-1)
#multiTool
multiTool.title=PDF Мулти инструмент
multiTool.header=PDF Мулти инструмент
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=View PDF
viewPdf.header=View PDF
viewPdf.title=Преглед на PDF
viewPdf.header=Преглед на PDF
#pageRemover
pageRemover.title=Премахване на страници
pageRemover.header=Премахване на PDF страници
pageRemover.pagesToDelete=Страници за изтриване (Въведете списък с номера на страници, разделени със запетая) :
pageRemover.submit=Изтриване на страници
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Изберете ъгъл на въртене (кратно
rotate.submit=Завъртане
#merge
#split-pdfs
split.title=Разделяне на PDF
split.header=Разделяне на PDF
split.desc.1=Числата, които избирате, са номера на страницата, на която искате да направите разделяне
split.desc.2=Така че избирането на 1,3,7-8 ще раздели документ от 10 страници на 6 отделни PDF файла с:
split.desc.2=Така че избирането на 1,3,7-9 ще раздели документ от 10 страници на 6 отделни PDF файла с:
split.desc.3=Документ #1: Страница 1
split.desc.4=Документ #2: Страница 2 и 3
split.desc.5=Документ #3: Страница 4, 5 и 6
split.desc.6=Документ #4: Страница 7
split.desc.7=Документ #5: Страница 8
split.desc.8=Документ #6: Страница 9 и 10
split.desc.5=Документ #3: Страница 4, 5, 6 и 7
split.desc.6=Документ #4: Страница 8
split.desc.7=Документ #5: Страница 9
split.desc.8=Документ #6: Страница 10
split.splitPages=Въведете страници за разделяне:
split.submit=Разделяне
@@ -858,6 +904,8 @@ watermark.selectText.7=Непрозрачност (0% - 100%):
watermark.selectText.8=Тип воден знак:
watermark.selectText.9=Изображение за воден знак:
watermark.submit=Добавяне на воден знак
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF към PDF/A
pdfToPDFA.header=PDF към PDF/A
pdfToPDFA.credit=Тази услуга използва OCRmyPDF за PDF/A преобразуване.
pdfToPDFA.submit=Преобразуване
pdfToPDFA.tip=В момента не работи за няколко входа наведнъж
pdfToPDFA.outputFormat=Изходен формат
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Преобразуване
#PDFToHTML
PDFToHTML.title=PDF към HTML
PDFToHTML.header=PDF към HTML
PDFToHTML.credit=Тази услуга използва LibreOffice за преобразуване на файлове.
PDFToHTML.credit=Тази услуга използва pdftohtml за преобразуване на файлове.
PDFToHTML.submit=Преобразуване
@@ -951,53 +1001,75 @@ PDFToXML.submit=Преобразуване
#PDFToCSV
PDFToCSV.title=PDF ??? CSV
PDFToCSV.header=PDF ??? CSV
PDFToCSV.prompt=Choose page to extract table
PDFToCSV.submit=????????
PDFToCSV.prompt=Изберете страница за извличане на таблица
PDFToCSV.submit=????
#split-by-size-or-count
split-by-size-or-count.title=Split PDF by Size or Count
split-by-size-or-count.header=Split PDF by Size or Count
split-by-size-or-count.type.label=Select Split Type
split-by-size-or-count.type.size=By Size
split-by-size-or-count.type.pageCount=By Page Count
split-by-size-or-count.type.docCount=By Document Count
split-by-size-or-count.value.label=Enter Value
split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5)
split-by-size-or-count.submit=Submit
split-by-size-or-count.title=Разделяне на PDF по размер или брой
split-by-size-or-count.header=Разделяне на PDF по размер или брой
split-by-size-or-count.type.label=Изберете тип разделяне
split-by-size-or-count.type.size=По размер
split-by-size-or-count.type.pageCount=По брой страници
split-by-size-or-count.type.docCount=По брой документи
split-by-size-or-count.value.label=Въведете стойност
split-by-size-or-count.value.placeholder=Въведете размер (напр. 2МБ или 3КБ) или брой (напр. 5)
split-by-size-or-count.submit=Изпращане
#overlay-pdfs
overlay-pdfs.header=Overlay PDF Files
overlay-pdfs.baseFile.label=Select Base PDF File
overlay-pdfs.overlayFiles.label=Select Overlay PDF Files
overlay-pdfs.mode.label=Select Overlay Mode
overlay-pdfs.mode.sequential=Sequential Overlay
overlay-pdfs.mode.interleaved=Interleaved Overlay
overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay
overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode)
overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1)
overlay-pdfs.position.label=Select Overlay Position
overlay-pdfs.position.foreground=Foreground
overlay-pdfs.position.background=Background
overlay-pdfs.submit=Submit
overlay-pdfs.header=Наслагване на PDF файлове
overlay-pdfs.baseFile.label=Изберете Основен PDF файл
overlay-pdfs.overlayFiles.label=Изберете наслагване на PDF файлове
overlay-pdfs.mode.label=Изберете режим на наслагване
overlay-pdfs.mode.sequential=Последователно наслагване
overlay-pdfs.mode.interleaved=Преплетено наслагване
overlay-pdfs.mode.fixedRepeat=Фиксирано наслагване при повторение
overlay-pdfs.counts.label=Брой наслагвания (за режим на фиксирано повторение)
overlay-pdfs.counts.placeholder=Въведете броя, разделени със запетая (напр. 2,3,1)
overlay-pdfs.position.label=Изберете позиция на наслагване
overlay-pdfs.position.foreground=Преден план
overlay-pdfs.position.background=Фон
overlay-pdfs.submit=Изпращане
#split-by-sections
split-by-sections.title=Split PDF by Sections
split-by-sections.header=Split PDF into Sections
split-by-sections.horizontal.label=Horizontal Divisions
split-by-sections.vertical.label=Vertical Divisions
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
split-by-sections.vertical.placeholder=Enter number of vertical divisions
split-by-sections.submit=Split PDF
split-by-sections.merge=Merge Into One PDF
split-by-sections.title=Разделяне на PDF по секции
split-by-sections.header=Разделяне на PDF на секции
split-by-sections.horizontal.label=Хоризонтални разделения
split-by-sections.vertical.label=Вертикални разделения
split-by-sections.horizontal.placeholder=Въведете брой хоризонтални деления
split-by-sections.vertical.placeholder=Въведете брой вертикални деления
split-by-sections.submit=Разделяне на PDF
split-by-sections.merge=Сливане в един PDF
#printFile
printFile.title=Печат на файл
printFile.header=Печат на файл на принтер
printFile.selectText.1=Изберете файл за печат
printFile.selectText.2=Въведете име на принтер
printFile.submit=Печат
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
licenses.header=3rd Party Licenses
licenses.module=Module
licenses.version=Version
licenses.license=License
licenses.nav=Лицензи
licenses.title=Лицензи на трети страни
licenses.header=Лицензи на трети страни
licenses.module=Модул
licenses.version=Версия
licenses.license=Лиценз
#error
error.sorry=Извинете за проблема!
error.needHelp=Нуждаете се от помощ / Открихте проблем?
error.contactTip=Ако все още имате проблеми, не се колебайте да се свържете с нас за помощ. Можете да изпратите запитване на нашата страница в GitHub или да се свържете с нас чрез Discord:
error.404.head=404 - Страницата не е намерена | Опа! Спънахме се в кода!
error.404.1=Изглежда не можем да намерим страницата, която търсите.
error.404.2=Нещо се обърка
error.github=Изпратете запитване в GitHub
error.showStack=Покажи проследяване на стека
error.copyStack=Копиране на проследяване на стека
error.githubSubmit=GitHub - Изпратете запитване
error.discordSubmit=Discord - Изпратете запитване за поддръжка

View File

@@ -54,9 +54,25 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual
downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=Converteix
navbar.security=Seguretat
navbar.other=Altres
navbar.favorite=Favorites
navbar.darkmode=Mode Fosc
navbar.pageOps=Operacions de Pàgina
navbar.language=Languages
navbar.settings=Opcions
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Opcions
settings.update=Actualització Disponible
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=Versió App:
settings.downloadOption.title=Trieu l'opció de descàrrega (per a descàrregues d'un sol fitxer no zip):
settings.downloadOption.1=Obre mateixa finestra
@@ -107,8 +132,9 @@ settings.downloadOption.3=Descarrega Arxiu
settings.zipThreshold=Comprimiu els fitxers quan el nombre de fitxers baixats superi
settings.signOut=Sortir
settings.accountSettings=Account Settings
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Change Credentials
changeCreds.header=Update Your Account Details
@@ -147,7 +173,7 @@ adminUserSettings.header=Usuari Admin Opcions Control
adminUserSettings.admin=Admin
adminUserSettings.user=Usuari
adminUserSettings.addUser=Afegir Usuari
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Rols
adminUserSettings.role=Rol
adminUserSettings.actions=Accions
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Desar Usuari
adminUserSettings.changeUserRole=Canvia el rol de l'usuari
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -422,6 +450,8 @@ login.rememberme=Recordar
login.invalid=Nom usuari / password no vàlid
login.locked=Compte bloquejat
login.signinTitle=Autenticat
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
login.oauth2AutoCreateDisabled=L'usuari de creació automàtica OAUTH2 està desactivat
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Convert To Single Page
pageExtracter.title=Extract Pages
pageExtracter.header=Extract Pages
pageExtracter.submit=Extract
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -673,6 +704,7 @@ repair.submit=Reparar
#flatten
flatten.title=Aplanar
flatten.header=Aplana els PDF
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Aplanar
@@ -720,6 +752,7 @@ extractImages.submit=Extreu
fileToPDF.title=Arxiu a PDF
fileToPDF.header=Converteix arxiu a PDF
fileToPDF.credit=Utilitza LibreOffice i Unoconv per a la conversió.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Els tipus de fitxers admesos haurien d'incloure el següent, però per obtenir una llista completa actualitzada dels formats compatibles, consulteu la documentació de LibreOffice
fileToPDF.submit=Converteix a PDF
@@ -756,11 +789,23 @@ merge.submit=Fusiona
pdfOrganiser.title=Organitzador de pàgines
pdfOrganiser.header=Organitzador de pàgines PDF
pdfOrganiser.submit=Reorganitza Pàgines
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=PDF Multi Tool
multiTool.header=PDF Multi Tool
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=View PDF
@@ -771,6 +816,7 @@ pageRemover.title=Eliminació Pàgines
pageRemover.header=Eliminació Pàgines PDF
pageRemover.pagesToDelete=Pàgines a esborrar (Números de pàgina) :
pageRemover.submit=Esborra Pàgines
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Selecciona l'angle de gir (en múltiples de 90 graus):
rotate.submit=Rota
#merge
#split-pdfs
split.title=Divideix PDF
split.header=Divideix PDF
split.desc.1=Els números seleccionats són el número de pàgina en què voleu fer la divisió
split.desc.2=Per tant, seleccionant 1,3,7-8 dividiria un document de 10 pàgines en 6 PDFS separats amb:
split.desc.2=Per tant, seleccionant 1,3,7-9 dividiria un document de 10 pàgines en 6 PDFS separats amb:
split.desc.3=Document #1: Pàgina 1
split.desc.4=Document #2: Pàgina 2 i 3
split.desc.5=Document #3: Pàgina 4, 5 i 6
split.desc.6=Document #4: Pàgina 7
split.desc.7=Document #5: Pàgina 8
split.desc.8=Document #6: Pàgina 9 i 10
split.desc.5=Document #3: Pàgina 4, 5, 6 i 7
split.desc.6=Document #4: Pàgina 8
split.desc.7=Document #5: Pàgina 9
split.desc.8=Document #6: Pàgina 10
split.splitPages=Introdueix pàgines per dividir-les:
split.submit=Divideix
@@ -858,6 +904,8 @@ watermark.selectText.7=Opacitat (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.submit=Afegir Marca d'Aigua
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF a PDF/A
pdfToPDFA.header=PDF a PDF/A
pdfToPDFA.credit=Utilitza OCRmyPDF per la conversió a PDF/A
pdfToPDFA.submit=Converteix
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Converteix
#PDFToHTML
PDFToHTML.title=PDF a HTML
PDFToHTML.header=PDF a HTML
PDFToHTML.credit=Utilitza LibreOffice per a la conversió d'Arxius.
PDFToHTML.credit=Utilitza pdftohtml per a la conversió d'Arxius.
PDFToHTML.submit=Converteix
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
split-by-sections.submit=Split PDF
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ true=Wahr
false=Falsch
unknown=Unbekannt
save=Speichern
saveToBrowser=Save to Browser
saveToBrowser=Im Browser speichern
close=Schließen
filesSelected=Dateien ausgewählt
noFavourites=Keine Favoriten hinzugefügt
@@ -46,7 +46,7 @@ green=Grün
blue=Blau
custom=benutzerdefiniert...
WorkInProgess=In Arbeit, funktioniert möglicherweise nicht oder ist fehlerhaft. Bitte melden Sie alle Probleme!
poweredBy=Powered by
poweredBy=Unterstützt von
yes=Ja
no=Nein
changedCredsMessage=Anmeldedaten geändert!
@@ -54,19 +54,37 @@ notAuthenticatedMessage=Benutzer nicht authentifiziert.
userNotFoundMessage=Benutzer nicht gefunden.
incorrectPasswordMessage=Das Passwort ist falsch.
usernameExistsMessage=Neuer Benutzername existiert bereits.
invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben und Zahlen enthalten.
invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein.
deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden.
deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden.
downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden
downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Fehler
oops=Hoppla!
help=Hilfe
goHomepage=Zur Startseite gehen
joinDiscord=Unserem Discord-Server beitreten
seeDockerHub=Docker Hub ansehen
visitGithub=GitHub-Repository besuchen
donate=Spenden
color=Farbe
sponsor=Sponsor
info=Info
###############
# Pipeline #
###############
pipeline.header=Pipeline-Menü (Alpha)
pipeline.header=Pipeline-Menü (Beta)
pipeline.uploadButton=Benutzerdefinierter Upload
pipeline.configureButton=Konfigurieren
pipeline.defaultOption=Benutzerdefiniert
pipeline.submitButton=Speichern
pipeline.help=Hilfe für Pipeline
pipeline.scanHelp=Hilfe zum Ordnerscan
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validieren
#############
# NAVBAR #
#############
navbar.convert=Konvertieren
navbar.security=Sicherheit
navbar.other=Anderes
navbar.favorite=Favorites
navbar.darkmode=Dunkler Modus
navbar.pageOps=Seitenoperationen
navbar.language=Languages
navbar.settings=Einstellungen
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Einstellungen
settings.update=Update verfügbar
settings.updateAvailable={0} ist die aktuelle installierte Version. Eine neue Version ({1}) ist verfügbar.
settings.appVersion=App-Version:
settings.downloadOption.title=Download-Option wählen (für einzelne Dateien, die keine Zip-Downloads sind):
settings.downloadOption.1=Im selben Fenster öffnen
@@ -107,8 +132,9 @@ settings.downloadOption.3=Datei herunterladen
settings.zipThreshold=Dateien komprimieren, wenn die Anzahl der heruntergeladenen Dateien überschritten wird
settings.signOut=Abmelden
settings.accountSettings=Kontoeinstellungen
settings.bored.help=Aktiviert das Easter-Egg-Spiel
settings.cacheInputs.name=Formulareingaben speichern
settings.cacheInputs.help=Aktivieren, um zuvor verwendete Eingaben für zukünftige Durchläufe zu speichern
changeCreds.title=Anmeldeinformationen ändern
changeCreds.header=Aktualisieren Sie Ihre Kontodaten
@@ -144,10 +170,10 @@ account.syncToAccount=Synchronisiere Konto <- Browser
adminUserSettings.title=Benutzerkontrolle
adminUserSettings.header=Administrator-Benutzerkontrolle
adminUserSettings.admin=Admin
adminUserSettings.admin=Administrator
adminUserSettings.user=Benutzer
adminUserSettings.addUser=Neuen Benutzer hinzufügen
adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben und Zahlen enthalten, keine Leerzeichen oder Sonderzeichen.
adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein.
adminUserSettings.roles=Rollen
adminUserSettings.role=Rolle
adminUserSettings.actions=Aktion
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo-Benutzer (Keine benutzerdefinierten Einstellunge
adminUserSettings.internalApiUser=Interner API-Benutzer
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
adminUserSettings.submit=Benutzer speichern
adminUserSettings.changeUserRole=Benutzerrolle ändern
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -168,96 +196,96 @@ home.searchBar=Suche nach Funktionen...
home.viewPdf.title=PDF anzeigen
home.viewPdf.desc=Anzeigen, Kommentieren, Text oder Bilder hinzufügen
viewPdf.tags=view,read,annotate,text,image
viewPdf.tags=anzeigen,lesen,kommentieren,text,bild
home.multiTool.title=PDF-Multitool
home.multiTool.desc=Seiten zusammenführen, drehen, neu anordnen und entfernen
multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side
home.merge.title=Zusammenführen
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen.
merge.tags=merge,Page operations,Back end,server side
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen
merge.tags=zusammenführen,seitenvorgänge,back end,serverseite
home.split.title=Aufteilen
home.split.desc=PDFs in mehrere Dokumente aufteilen.
split.tags=Page operations,divide,Multi Page,cut,server side
home.split.desc=PDFs in mehrere Dokumente aufteilen
split.tags=seitenoperationen,teilen,mehrseitig,ausschneiden,serverseitig
home.rotate.title=Drehen
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach.
rotate.tags=server side
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach
rotate.tags=serverseitig
home.imageToPdf.title=Bild zu PDF
home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF.
imageToPdf.tags=conversion,img,jpg,picture,photo
home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF
imageToPdf.tags=konvertierung,img,jpg,bild,foto
home.pdfToImage.title=PDF zu Bild
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF).
pdfToImage.tags=conversion,img,jpg,picture,photo
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF)
pdfToImage.tags=konvertierung,img,jpg,bild,foto
home.pdfOrganiser.title=Organisieren
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern.
pdfOrganiser.tags=duplex,even,odd,sort,move
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern
pdfOrganiser.tags=duplex,gerade,ungerade,sortieren,verschieben
home.addImage.title=Bild einfügen
home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit).
addImage.tags=img,jpg,picture,photo
home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit)
addImage.tags=img,jpg,bild,foto
home.watermark.title=Wasserzeichen hinzufügen
home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu.
watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo
home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu
watermark.tags=text,wiederholend,beschriftung,besitzen,urheberrecht,marke,img,jpg,bild,foto
home.permissions.title=Berechtigungen ändern
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern.
permissions.tags=read,write,edit,print
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern
permissions.tags=lesen,schreiben,bearbeiten,drucken
home.removePages.title=Entfernen
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen.
removePages.tags=Remove pages,delete pages
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen
removePages.tags=seiten entfernen,seiten löschen
home.addPassword.title=Passwort hinzufügen
home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln.
addPassword.tags=secure,security
home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln
addPassword.tags=sicher,sicherheit
home.removePassword.title=Passwort entfernen
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen.
removePassword.tags=secure,Decrypt,security,unpassword,delete password
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen
removePassword.tags=sichern,entschlüsseln,sicherheit,passwort aufheben,passwort löschen
home.compressPdfs.title=Komprimieren
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
compressPdfs.tags=squish,small,tiny
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren
compressPdfs.tags=komprimieren,verkleinern,minimieren
home.changeMetadata.title=Metadaten ändern
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
changeMetadata.tags==titel,autor,datum,erstellung,uhrzeit,herausgeber,produzent,statistiken
home.fileToPDF.title=Datei in PDF konvertieren
home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr)
fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint
fileToPDF.tags=transformation,format,dokument,bild,folie,text,konvertierung,büro,dokumente,word,excel,powerpoint
home.ocr.title=Führe OCR/Cleanup-Scans aus
home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu.
ocr.tags=recognition,text,image,scan,read,identify,detection,editable
home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu
ocr.tags=erkennung,text,bild,scannen,lesen,identifizieren,erkennung,bearbeitbar
home.extractImages.title=Bilder extrahieren
home.extractImages.desc=Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Archiv
extractImages.tags=picture,photo,save,archive,zip,capture,grab
extractImages.tags=bild,foto,speichern,archivieren,zippen,erfassen,greifen
home.pdfToPDFA.title=PDF zu PDF/A konvertieren
home.pdfToPDFA.desc=PDF zu PDF/A für Langzeitarchivierung konvertieren
pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation
pdfToPDFA.tags=archiv,langfristig,standard,konvertierung,speicherung,aufbewahrung
home.PDFToWord.title=PDF zu Word
home.PDFToWord.desc=PDF in Word-Formate konvertieren (DOC, DOCX und ODT)
PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile
PDFToWord.tags=doc,docx,odt,word,transformation,format,konvertierung,office,microsoft,docfile
home.PDFToPresentation.title=PDF zu Präsentation
home.PDFToPresentation.desc=PDF in Präsentationsformate konvertieren (PPT, PPTX und ODP)
PDFToPresentation.tags=slides,show,office,microsoft
PDFToPresentation.tags=folien,show,büro,microsoft
home.PDFToText.title=PDF in Text/RTF
home.PDFToText.desc=PDF in Text- oder RTF-Format konvertieren
@@ -265,88 +293,88 @@ PDFToText.tags=richformat,richtextformat,rich text format
home.PDFToHTML.title=PDF in HTML
home.PDFToHTML.desc=PDF in HTML-Format konvertieren
PDFToHTML.tags=web content,browser friendly
PDFToHTML.tags=webinhalte,browserfreundlich
home.PDFToXML.title=PDF in XML
home.PDFToXML.desc=PDF in XML-Format konvertieren
PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert
PDFToXML.tags=datenextraktion,strukturierter inhalt,interop,transformation,konvertierung
home.ScannerImageSplit.title=Gescannte Fotos erkennen/aufteilen
home.ScannerImageSplit.desc=Teilt mehrere Fotos innerhalb eines Fotos/PDF
ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize
ScannerImageSplit.tags=separat,automatische erkennung,scans,mehrere fotos,organisieren
home.sign.title=Signieren
home.sign.desc=Fügt PDF-Signaturen durch Zeichnung, Text oder Bild hinzu
sign.tags=authorize,initials,drawn-signature,text-sign,image-signature
sign.tags=autorisieren,initialen,gezeichnete signatur,textzeichen,bildsignatur
home.flatten.title=Abflachen
home.flatten.desc=Alle interaktiven Elemente und Formulare aus einem PDF entfernen
flatten.tags=static,deactivate,non-interactive,streamline
flatten.tags=statisch,deaktivieren,nicht interaktiv,optimieren
home.repair.title=Reparatur
home.repair.desc=Versucht, ein beschädigtes/kaputtes PDF zu reparieren
repair.tags=fix,restore,correction,recover
repair.tags=reparieren,wiederherstellen,korrigieren,wiederherstellen
home.removeBlanks.title=Leere Seiten entfernen
home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument
removeBlanks.tags=cleanup,streamline,non-content,organize
removeBlanks.tags=aufräumen,rationalisieren,nicht inhaltsreich,organisieren
home.removeAnnotations.title=Anmerkungen entfernen
home.removeAnnotations.desc=Entfernt alle Kommentare/Anmerkungen aus einem PDF
removeAnnotations.tags=comments,highlight,notes,markup,remove
removeAnnotations.tags=kommentare,hervorheben,notizen,markieren,entfernen
home.compare.title=Vergleichen
home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an
compare.tags=differentiate,contrast,changes,analysis
compare.tags=differenzieren,kontrastieren,verändern,analysieren
home.certSign.title=Mit Zertifikat signieren
home.certSign.desc=Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren
certSign.tags=authenticate,PEM,P12,official,encrypt
certSign.tags=authentifizieren,pem,p12,offiziell,verschlüsseln
home.pageLayout.title=Mehrseitiges Layout
home.pageLayout.desc=Mehrere Seiten eines PDF zu einer Seite zusammenführen
pageLayout.tags=merge,composite,single-view,organize
pageLayout.tags=zusammenführen,zusammensetzen,einzelansicht,organisieren
home.scalePages.title=Seitengröße/Skalierung anpassen
home.scalePages.desc=Größe/Skalierung der Seite und/oder des Inhalts ändern
scalePages.tags=resize,modify,dimension,adapt
scalePages.tags=größe ändern,ändern,dimensionieren,anpassen
home.pipeline.title=Pipeline (Fortgeschritten)
home.pipeline.desc=Mehrere Aktionen auf ein PDF anwenden, definiert durch ein Pipeline Skript
pipeline.tags=automate,sequence,scripted,batch-process
pipeline.tags=automatisieren,sequenzieren,skriptgesteuert,batch prozess
home.add-page-numbers.title=Seitenzahlen hinzufügen
home.add-page-numbers.desc=Hinzufügen von Seitenzahlen an einer bestimmten Stelle
add-page-numbers.tags=paginate,label,organize,index
add-page-numbers.tags=paginieren,beschriften,organisieren,indizieren
home.auto-rename.title=PDF automatisch umbenennen
home.auto-rename.desc=PDF-Datei anhand von erkannten Kopfzeilen umbenennen
auto-rename.tags=auto-detect,header-based,organize,relabel
auto-rename.tags=automatisch erkennen,header basiert,organisieren,neu kennzeichnen
home.adjust-contrast.title=Farben/Kontrast anpassen
home.adjust-contrast.desc=Kontrast, Sättigung und Helligkeit einer PDF anpassen
adjust-contrast.tags=color-correction,tune,modify,enhance
adjust-contrast.tags=farbkorrektur,abstimmung,änderung,verbesserung
home.crop.title=PDF zuschneiden
home.crop.desc=PDF zuschneiden um die Größe zu verändern (Text bleibt erhalten!)
crop.tags=trim,shrink,edit,shape
crop.tags=trimmen,verkleinern,bearbeiten,formen
home.autoSplitPDF.title=PDF automatisch teilen
home.autoSplitPDF.desc=Physisch gescannte PDF anhand von Splitter-Seiten und QR-Codes aufteilen
autoSplitPDF.tags=QR-based,separate,scan-segment,organize
autoSplitPDF.tags=qr basiert,trennen,segment scannen,organisieren
home.sanitizePdf.title=PDF Bereinigen
home.sanitizePdf.desc=Entfernen von Skripten und anderen Elementen aus PDF-Dateien
sanitizePdf.tags=clean,secure,safe,remove-threats
sanitizePdf.tags=sauber,sicher,sicher,bedrohungen entfernen
home.URLToPDF.title=URL/Website zu PDF
home.URLToPDF.desc=Konvertiert jede http(s)URL zu PDF
URLToPDF.tags=web-capture,save-page,web-to-doc,archive
URLToPDF.tags=web capture,seite speichern,web to doc,archiv
home.HTMLToPDF.title=HTML zu PDF
home.HTMLToPDF.desc=Konvertiert jede HTML-Datei oder Zip-Archiv zu PDF
HTMLToPDF.tags=markup,web-content,transformation,convert
HTMLToPDF.tags=markup,webinhalt,transformation,konvertierung
home.MarkdownToPDF.title=Markdown zu PDF
@@ -361,17 +389,17 @@ getPdfInfo.tags=infomation,daten,statistik
home.extractPage.title=Seite(n) extrahieren
home.extractPage.desc=Extrahiert ausgewählte Seiten aus einer PDF
extractPage.tags=extrahieren
extractPage.tags=extrahieren,seite
home.PdfToSinglePage.title=PDF zu einer Seite zusammenfassen
home.PdfToSinglePage.desc=Fügt alle PDF-Seiten zu einer einzigen großen Seite zusammen
PdfToSinglePage.tags=einzelseite
PdfToSinglePage.tags=einzelseite,zusammenfassen
home.showJS.title=Javascript anzeigen
home.showJS.desc=Alle Javascript Funktionen in einer PDF anzeigen
showJS.tags=JS
showJS.tags=js,javascript
home.autoRedact.title=Automatisch zensieren/schwärzen
home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text
@@ -379,7 +407,7 @@ autoRedact.tags=zensieren,schwärzen
home.tableExtraxt.title=Tabelle extrahieren
home.tableExtraxt.desc=Tabelle aus PDF in CSV extrahieren
tableExtraxt.tags=CSV
tableExtraxt.tags=CSV,tabelle,extrahieren
home.autoSizeSplitPDF.title=Teilen nach Größe/Anzahl
@@ -397,16 +425,16 @@ split-by-sections.tags=abschnitte,teilen,bearbeiten
home.AddStampRequest.title=Stempel zu PDF hinzufügen
home.AddStampRequest.desc=Fügen Sie an festgelegten Stellen Text oder Bildstempel hinzu
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
AddStampRequest.tags=stempeln,bild hinzufügen,bild zentrieren,wasserzeichen,pdf,einbetten,anpassen
home.PDFToBook.title=PDF to Book
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.PDFToBook.title=PDF zum Buch
home.PDFToBook.desc=Konvertiert PDF mit Calibre in Buch-/Comic-Formate
PDFToBook.tags=buch,comic,calibre,convert,manga,amazon,kindle
home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.BookToPDF.title=Buch als PDF
home.BookToPDF.desc=Konvertiert Buch-/Comic-Formate mithilfe von Calibre in PDF
BookToPDF.tags=buch,comic,calibre,convert,manga,amazon,kindle
###########################
@@ -422,6 +450,8 @@ login.rememberme=Angemeldet bleiben
login.invalid=Benutzername oder Passwort ungültig.
login.locked=Ihr Konto wurde gesperrt.
login.signinTitle=Bitte melden Sie sich an.
login.ssoSignIn=Anmeldung per Single Sign-On
login.oauth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert
#auto-redact
@@ -432,7 +462,7 @@ autoRedact.textsToRedactLabel=Zu zensierender Text (einer pro Zeile)
autoRedact.textsToRedactPlaceholder=z.B. \nVertraulich \nStreng geheim
autoRedact.useRegexLabel=Regex verwenden
autoRedact.wholeWordSearchLabel=Ganzes Wort suchen
autoRedact.customPaddingLabel=Benutzerdefinierte Extra-Padding
autoRedact.customPaddingLabel=Zensierten Bereich vergrößern
autoRedact.convertPDFToImageLabel=PDF in PDF-Bild konvertieren (zum Entfernen von Text hinter dem Kasten)
autoRedact.submitButton=Zensieren
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Zusammenfassen
pageExtracter.title=Seiten extrahieren
pageExtracter.header=Seiten extrahieren
pageExtracter.submit=Extrahieren
pageExtracter.placeholder=(z.B. 1,2,8 oder 4,7,12-16 oder 2n-1)
#getPdfInfo
@@ -485,16 +516,16 @@ HTMLToPDF.header=HTML zu PDF
HTMLToPDF.help=Akzeptiert HTML-Dateien und ZIPs mit html/css/images etc.
HTMLToPDF.submit=Konvertieren
HTMLToPDF.credit=Verwendet WeasyPrint
HTMLToPDF.zoom=Zoomstufe zur Darstellung der Website.
HTMLToPDF.pageWidth=Breite der Seite in Zentimetern. (Leer auf Standard)
HTMLToPDF.pageHeight=Höhe der Seite in Zentimetern. (Leer auf Standard)
HTMLToPDF.marginTop=Oberer Rand der Seite in Millimetern. (Leer auf Standard)
HTMLToPDF.marginBottom=Unterer Rand der Seite in Millimetern. (Leer auf Standard)
HTMLToPDF.marginLeft=Linker Rand der Seite in Millimetern. (Leer auf Standard)
HTMLToPDF.marginRight=Linker Rand der Seite in Millimetern. (Leer auf Standard)
HTMLToPDF.printBackground=Den Hintergrund der Website rendern.
HTMLToPDF.zoom=Zoomstufe zur Darstellung der Website
HTMLToPDF.pageWidth=Breite der Seite in Zentimetern (Leer auf Standard)
HTMLToPDF.pageHeight=Höhe der Seite in Zentimetern (Leer auf Standard)
HTMLToPDF.marginTop=Oberer Rand der Seite in Millimetern (Leer auf Standard)
HTMLToPDF.marginBottom=Unterer Rand der Seite in Millimetern (Leer auf Standard)
HTMLToPDF.marginLeft=Linker Rand der Seite in Millimetern (Leer auf Standard)
HTMLToPDF.marginRight=Linker Rand der Seite in Millimetern (Leer auf Standard)
HTMLToPDF.printBackground=Den Hintergrund der Website rendern
HTMLToPDF.defaultHeader=Standardkopfzeile aktivieren (Name und Seitenzahl)
HTMLToPDF.cssMediaType=CSS-Medientyp der Seite ändern.
HTMLToPDF.cssMediaType=CSS-Medientyp der Seite ändern
HTMLToPDF.none=Keine
HTMLToPDF.print=Drucken
HTMLToPDF.screen=Bildschirm
@@ -508,7 +539,7 @@ AddStampRequest.stampText=Stempeltext
AddStampRequest.stampImage=Stampelbild
AddStampRequest.alphabet=Alphabet
AddStampRequest.fontSize=Schriftart/Bildgröße
AddStampRequest.rotation=Rotation
AddStampRequest.rotation=Drehung
AddStampRequest.opacity=Deckkraft
AddStampRequest.position=Position
AddStampRequest.overrideX=X-Koordinate überschreiben
@@ -595,8 +626,8 @@ pageLayout.submit=Abschicken
#scalePages
scalePages.title=Seitengröße anpassen
scalePages.header=Seitengröße anpassen
scalePages.pageSize=Format der Seiten des Dokuments.
scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite.
scalePages.pageSize=Format der Seiten des Dokuments
scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite
scalePages.submit=Abschicken
@@ -673,6 +704,7 @@ repair.submit=Reparieren
#flatten
flatten.title=Abflachen
flatten.header=PDFs reduzieren
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Abflachen
@@ -720,6 +752,7 @@ extractImages.submit=Extrahieren
fileToPDF.title=Datei in PDF
fileToPDF.header=Beliebige Dateien in PDF konvertieren
fileToPDF.credit=Dieser Dienst verwendet LibreOffice und Unoconv für die Dateikonvertierung.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Unterstützte Dateitypen sollten die folgenden enthalten, eine vollständige aktualisierte Liste der unterstützten Formate finden Sie jedoch in der LibreOffice-Dokumentation
fileToPDF.submit=In PDF konvertieren
@@ -739,7 +772,7 @@ compress.submit=Komprimieren
#Add image
addImage.title=Bild hinzufügen
addImage.header=Ein Bild einfügen
addImage.everyPage=Jede Seite?
addImage.everyPage=In jede Seite einfügen?
addImage.upload=Bild hinzufügen
addImage.submit=Bild hinzufügen
@@ -756,11 +789,23 @@ merge.submit=Zusammenführen
pdfOrganiser.title=Seiten anordnen
pdfOrganiser.header=PDF Seitenorganisation
pdfOrganiser.submit=Seiten anordnen
pdfOrganiser.mode=Modus
pdfOrganiser.mode.1=Benutzerdefinierte Seitenreihenfolge
pdfOrganiser.mode.2=Umgekehrte Reihenfolge
pdfOrganiser.mode.3=Duplex-Sortierung
pdfOrganiser.mode.4=Heftsortierung
pdfOrganiser.mode.5=Seitenheftungs-Heftsortierung
pdfOrganiser.mode.6=Ungerade-Gerade-Teilung
pdfOrganiser.mode.7=Erste entfernen
pdfOrganiser.mode.8=Letzte entfernen
pdfOrganiser.mode.9=Erste und letzte entfernen
pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)
#multiTool
multiTool.title=PDF-Multitool
multiTool.header=PDF-Multitool
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=PDF anzeigen
@@ -771,6 +816,7 @@ pageRemover.title=Seiten entfernen
pageRemover.header=PDF Seiten entfernen
pageRemover.pagesToDelete=Seiten zu entfernen (geben Sie eine Kommagetrennte Liste der Seitenzahlen an):
pageRemover.submit=Seiten löschen
pageRemover.placeholder=(z.B. 1,2,6 oder 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Wählen Sie den Winkel (in Vielfachen von 90 Grad):
rotate.submit=Herunterladen
#merge
#split-pdfs
split.title=PDF aufteilen
split.header=PDF aufteilen
split.desc.1=Die Nummern, die Sie auswählen, sind die Seitenzahlen, an denen Sie aufteilen möchten.
split.desc.2=So würde die Auswahl von 1,3,7-8 ein 10-seitiges Dokument in 6 separate PDFs aufteilen, mit:
split.desc.2=So würde die Auswahl von 1,3,7-9 ein 10-seitiges Dokument in 6 separate PDFs aufteilen, mit:
split.desc.3=Dokument #1: Seite 1
split.desc.4=Dokument #2: Seite 2 und 3
split.desc.5=Dokument #3: Seite 4, 5 und 6
split.desc.6=Dokument #4: Seite 7
split.desc.7=Dokument #5: Seite 8
split.desc.8=Dokument #6: Seite 9 und 10
split.desc.5=Dokument #3: Seite 4, 5, 6 und 7
split.desc.6=Dokument #4: Seite 8
split.desc.7=Dokument #5: Seite 9
split.desc.8=Dokument #6: Seite 10
split.splitPages=Geben Sie die Seiten an, an denen aufgeteilt werden soll:
split.submit=Aufteilen
@@ -858,6 +904,8 @@ watermark.selectText.7=Deckkraft (0% - 100 %):
watermark.selectText.8=Wasserzeichen Typ:
watermark.selectText.9=Wasserzeichen-Bild:
watermark.submit=Wasserzeichen hinzufügen
watermark.type.1=Text
watermark.type.2=Bild
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF zu PDF/A
pdfToPDFA.header=PDF zu PDF/A
pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF für die PDF/A-Konvertierung
pdfToPDFA.submit=Konvertieren
pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten.
pdfToPDFA.outputFormat=Ausgabeformat
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Konvertieren
#PDFToHTML
PDFToHTML.title=PDF zu HTML
PDFToHTML.header=PDF zu HTML
PDFToHTML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
PDFToHTML.credit=Dieser Dienst verwendet pdftohtml für die Dateikonvertierung.
PDFToHTML.submit=Konvertieren
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Anzahl vertikaler Teiler eingeben
split-by-sections.submit=PDF teilen
split-by-sections.merge=In eine PDF zusammenfügen
#printFile
printFile.title=Datei drucken
printFile.header=Datei an Drucker senden
printFile.selectText.1=Wähle die auszudruckende Datei
printFile.selectText.2=Druckernamen eingeben
printFile.submit=Drucken
#licenses
licenses.nav=Lizenzen
licenses.title=Lizenzen von Drittanbietern
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=Lizenz
#error
error.sorry=Entschuldigung für das Problem!
error.needHelp=Brauchst du Hilfe / Ein Problem gefunden?
error.contactTip=Wenn du weiterhin Probleme hast, zögere nicht, uns um Hilfe zu bitten. Du kannst ein Ticket auf unserer GitHub-Seite einreichen oder uns über Discord kontaktieren:
error.404.head=404 - Seite nicht gefunden | Ups, wir sind im Code gestolpert!
error.404.1=Wir können die gesuchte Seite nicht finden.
error.404.2=Etwas ist schiefgelaufen
error.github=Ein Ticket auf GitHub einreichen
error.showStack=Stack-Trace anzeigen
error.copyStack=Stack-Trace kopieren
error.githubSubmit=GitHub - Ein Ticket einreichen
error.discordSubmit=Discord - Unterstützungsbeitrag einreichen

File diff suppressed because it is too large Load Diff

View File

@@ -54,9 +54,25 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Cannot downgrade current user's role
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=Convert
navbar.security=Security
navbar.other=Miscellaneous
navbar.favorite=Favorites
navbar.darkmode=Dark Mode
navbar.pageOps=Page Operations
navbar.language=Languages
navbar.settings=Settings
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Settings
settings.update=Update available
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=App Version:
settings.downloadOption.title=Choose download option (For single file non zip downloads):
settings.downloadOption.1=Open in same window
@@ -107,8 +132,9 @@ settings.downloadOption.3=Download file
settings.zipThreshold=Zip files when the number of downloaded files exceeds
settings.signOut=Sign Out
settings.accountSettings=Account Settings
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Change Credentials
changeCreds.header=Update Your Account Details
@@ -147,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin
adminUserSettings.user=User
adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles
adminUserSettings.role=Role
adminUserSettings.actions=Actions
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Change User's Role
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -422,6 +450,8 @@ login.rememberme=Remember me
login.invalid=Invalid username or password.
login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Convert To Single Page
pageExtracter.title=Extract Pages
pageExtracter.header=Extract Pages
pageExtracter.submit=Extract
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -672,7 +703,8 @@ repair.submit=Repair
#flatten
flatten.title=Flatten
flatten.header=Flatten PDFs
flatten.header=Flatten PDF
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Flatten
@@ -720,6 +752,7 @@ extractImages.submit=Extract
fileToPDF.title=File to PDF
fileToPDF.header=Convert any file to PDF
fileToPDF.credit=This service uses LibreOffice and Unoconv for file conversion.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Supported file types should include the below however for a full updated list of supported formats, please refer to the LibreOffice documentation
fileToPDF.submit=Convert to PDF
@@ -756,11 +789,23 @@ merge.submit=Merge
pdfOrganiser.title=Page Organiser
pdfOrganiser.header=PDF Page Organiser
pdfOrganiser.submit=Rearrange Pages
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=PDF Multi Tool
multiTool.header=PDF Multi Tool
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=View PDF
@@ -771,6 +816,7 @@ pageRemover.title=Page Remover
pageRemover.header=PDF Page remover
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
pageRemover.submit=Delete Pages
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
rotate.submit=Rotate
#merge
#split-pdfs
split.title=Split PDF
split.header=Split PDF
split.desc.1=The numbers you select are the page number you wish to do a split on
split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with:
split.desc.2=As such selecting 1,3,7-9 would split a 10 page document into 6 separate PDFS with:
split.desc.3=Document #1: Page 1
split.desc.4=Document #2: Page 2 and 3
split.desc.5=Document #3: Page 4, 5 and 6
split.desc.6=Document #4: Page 7
split.desc.7=Document #5: Page 8
split.desc.8=Document #6: Page 9 and 10
split.desc.5=Document #3: Page 4, 5, 6 and 7
split.desc.6=Document #4: Page 8
split.desc.7=Document #5: Page 9
split.desc.8=Document #6: Page 10
split.splitPages=Enter pages to split on:
split.submit=Split
@@ -858,6 +904,8 @@ watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.submit=Add Watermark
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=PDF To PDF/A
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Convert
#PDFToHTML
PDFToHTML.title=PDF to HTML
PDFToHTML.header=PDF to HTML
PDFToHTML.credit=This service uses LibreOffice for file conversion.
PDFToHTML.credit=This service uses pdftohtml for file conversion.
PDFToHTML.submit=Convert
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
split-by-sections.submit=Split PDF
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

View File

@@ -54,9 +54,25 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Cannot downgrade current user's role
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=Convert
navbar.security=Security
navbar.other=Miscellaneous
navbar.favorite=Favorites
navbar.darkmode=Dark Mode
navbar.pageOps=Page Operations
navbar.language=Languages
navbar.settings=Settings
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Settings
settings.update=Update available
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=App Version:
settings.downloadOption.title=Choose download option (For single file non zip downloads):
settings.downloadOption.1=Open in same window
@@ -107,8 +132,9 @@ settings.downloadOption.3=Download file
settings.zipThreshold=Zip files when the number of downloaded files exceeds
settings.signOut=Sign Out
settings.accountSettings=Account Settings
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Change Credentials
changeCreds.header=Update Your Account Details
@@ -147,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin
adminUserSettings.user=User
adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles
adminUserSettings.role=Role
adminUserSettings.actions=Actions
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Change User's Role
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -312,7 +340,7 @@ home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of a page and/or its contents.
scalePages.tags=resize,modify,dimension,adapt
home.pipeline.title=Pipeline (Advanced)
home.pipeline.title=Pipeline
home.pipeline.desc=Run multiple actions on PDFs by defining pipeline scripts
pipeline.tags=automate,sequence,scripted,batch-process
@@ -364,7 +392,7 @@ home.extractPage.desc=Extracts select pages from PDF
extractPage.tags=extract
home.PdfToSinglePage.title=PDF to Single Large Page
home.PdfToSinglePage.title=Single Large Page
home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
PdfToSinglePage.tags=single page
@@ -422,6 +450,8 @@ login.rememberme=Remember me
login.invalid=Invalid username or password.
login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Convert To Single Page
pageExtracter.title=Extract Pages
pageExtracter.header=Extract Pages
pageExtracter.submit=Extract
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -673,6 +704,7 @@ repair.submit=Repair
#flatten
flatten.title=Flatten
flatten.header=Flatten PDFs
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Flatten
@@ -720,6 +752,7 @@ extractImages.submit=Extract
fileToPDF.title=File to PDF
fileToPDF.header=Convert any file to PDF
fileToPDF.credit=This service uses LibreOffice and Unoconv for file conversion.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Supported file types should include the below however for a full updated list of supported formats, please refer to the LibreOffice documentation
fileToPDF.submit=Convert to PDF
@@ -756,11 +789,23 @@ merge.submit=Merge
pdfOrganiser.title=Page Organizer
pdfOrganiser.header=PDF Page Organizer
pdfOrganiser.submit=Rearrange Pages
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=PDF Multi Tool
multiTool.header=PDF Multi Tool
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=View PDF
@@ -771,6 +816,7 @@ pageRemover.title=Page Remover
pageRemover.header=PDF Page remover
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
pageRemover.submit=Delete Pages
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
rotate.submit=Rotate
#merge
#split-pdfs
split.title=Split PDF
split.header=Split PDF
split.desc.1=The numbers you select are the page number you wish to do a split on
split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with:
split.desc.2=As such selecting 1,3,7-9 would split a 10 page document into 6 separate PDFS with:
split.desc.3=Document #1: Page 1
split.desc.4=Document #2: Page 2 and 3
split.desc.5=Document #3: Page 4, 5 and 6
split.desc.6=Document #4: Page 7
split.desc.7=Document #5: Page 8
split.desc.8=Document #6: Page 9 and 10
split.desc.5=Document #3: Page 4, 5, 6, 7
split.desc.6=Document #4: Page 8
split.desc.7=Document #5: Page 9
split.desc.8=Document #6: Page 10
split.splitPages=Enter pages to split on:
split.submit=Split
@@ -858,6 +904,8 @@ watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.submit=Add Watermark
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=PDF To PDF/A
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Convert
#PDFToHTML
PDFToHTML.title=PDF to HTML
PDFToHTML.header=PDF to HTML
PDFToHTML.credit=This service uses LibreOffice for file conversion.
PDFToHTML.credit=This service uses pdftohtml for file conversion.
PDFToHTML.submit=Convert
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
split-by-sections.submit=Split PDF
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

View File

@@ -17,11 +17,11 @@ true=Verdadero
false=Falso
unknown=Desconocido
save=Guardar
saveToBrowser=Save to Browser
saveToBrowser=Guardar en el Navegador
close=Cerrar
filesSelected=archivos seleccionados
noFavourites=No se agregaron favoritos
downloadComplete=Download Complete
downloadComplete=Descarga finalizada
bored=¿Cansado de esperar?
alphabet=Alfabeto
downloadPdf=Descargar PDF
@@ -54,9 +54,25 @@ notAuthenticatedMessage=Usuario no autentificado.
userNotFoundMessage=Usuario no encontrado.
incorrectPasswordMessage=La contraseña actual no es correcta.
usernameExistsMessage=El nuevo nombre de usuario está en uso.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
invalidUsernameMessage=Nombre de usuario no válido, el nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso.
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual
downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Ups!
help=Help
goHomepage=Ir a la página principal
joinDiscord=Únase a nuestro servidor Discord
seeDockerHub=Ver Docker Hub
visitGithub=Visitar Repositorio de Github
donate=Donar
color=Color
sponsor=Patrocinador
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Cargar personalización
pipeline.configureButton=Configurar
pipeline.defaultOption=Personalizar
pipeline.submitButton=Enviar
pipeline.help=Ayuda de Canalización
pipeline.scanHelp=Ayuda de escaneado de carpetas
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validar
#############
# NAVBAR #
#############
navbar.convert=Convertir
navbar.security=Seguridad
navbar.other=Otro
navbar.favorite=Favorites
navbar.darkmode=Modo oscuro
navbar.pageOps=Operaciones de página
navbar.language=Languages
navbar.settings=Configuración
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Configuración
settings.update=Actualización disponible
settings.updateAvailable={0} es la versión instalada. Hay disponible una versión nueva ({1}).
settings.appVersion=Versión de la aplicación:
settings.downloadOption.title=Elegir la opción de descarga (para descargas de un solo archivo sin ZIP):
settings.downloadOption.1=Abrir en la misma ventana
@@ -107,12 +132,13 @@ settings.downloadOption.3=Descargar el archivo
settings.zipThreshold=Archivos ZIP cuando excede el número de archivos descargados
settings.signOut=Desconectar
settings.accountSettings=Configuración de la cuenta
settings.bored.help=Habilita el juego del huevo de pascua
settings.cacheInputs.name=Guardar entradas del formulario
settings.cacheInputs.help=Habilitar guardar entradas previamente utilizadas para futuras acciones
changeCreds.title=Cambiar Credenciales
changeCreds.header=Actualice los detalles de su cuenta
changeCreds.changePassword=You are using default login credentials. Please enter a new password
changeCreds.changePassword=Está usando las credenciales de inicio de sesión por defecto. Por favor, introduzca una contraseña nueva
changeCreds.newUsername=Nuevo usuario
changeCreds.oldPassword=Contraseña actual
changeCreds.newPassword=Nueva contraseña
@@ -147,17 +173,19 @@ adminUserSettings.header=Configuración de control de usuario administrador
adminUserSettings.admin=Administrador
adminUserSettings.user=Usuario
adminUserSettings.addUser=Añadir Nuevo Usuario
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
adminUserSettings.roles=Roles
adminUserSettings.role=Rol
adminUserSettings.actions=Acciones
adminUserSettings.apiUser=Usuario limitado de API
adminUserSettings.extraApiUser=Additional Limited API User
adminUserSettings.extraApiUser=Otro usuario limitado de API
adminUserSettings.webOnlyUser=Usuario solo web
adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.demoUser=Usuario Demo (Sin ajustes personalizados)
adminUserSettings.internalApiUser=Usuario interno de API
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
adminUserSettings.submit=Guardar Usuario
adminUserSettings.changeUserRole=Cambiar rol de usuario
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -232,7 +260,7 @@ compressPdfs.tags=aplastar,pequeño,diminuto
home.changeMetadata.title=Cambiar metadatos
home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF
changeMetadata.tags==Título,autor,fecha,creación,hora,editorial,productor,estadísticas
changeMetadata.tags=título,autor,fecha,creación,hora,editorial,productor,estadísticas
home.fileToPDF.title=Convertir archivo a PDF
home.fileToPDF.desc=Convertir casi cualquier archivo a PDF (DOCX, PNG, XLS, PPT, TXT y más)
@@ -375,7 +403,7 @@ showJS.tags=JS
home.autoRedact.title=Auto Redactar
home.autoRedact.desc=Redactar automáticamente (ocultar) texto en un PDF según el texto introducido
autoRedact.tags=Redact,Hide,black out,black,marker,hidden
autoRedact.tags=Redactar,Ocultar,ocultar,negro,subrayador,oculto
home.tableExtraxt.title=PDF a CSV
home.tableExtraxt.desc=Extraer Tablas de un PDF convirtiéndolas a CSV
@@ -395,18 +423,18 @@ home.split-by-sections.title=Dividir PDF por Secciones
home.split-by-sections.desc=Dividir cada página de un PDF en secciones verticales y horizontales más pequeñas
split-by-sections.tags=Dividir sección, Dividir, Personalizar
home.AddStampRequest.title=Add Stamp to PDF
home.AddStampRequest.desc=Add text or add image stamps at set locations
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
home.AddStampRequest.title=Añadir Sello a PDF
home.AddStampRequest.desc=Añadir texto o sello de imagen en ubicaciones específicas
AddStampRequest.tags=Sello, Añadir imagen, centrar imagen, Marca de agua, PDF, Incrustar, Personalizar
home.PDFToBook.title=PDF to Book
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.PDFToBook.title=PDF a Libro
home.PDFToBook.desc=Convierte PDF a formatos de Libro/Cómic usando Calibre
PDFToBook.tags=Libro,Cómic,Calibre,Convertir,Manga,Amazon,Kindle
home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.BookToPDF.title=Libro a PDF
home.BookToPDF.desc=Convierte formatos de Libro/Cómic a PDF usando Calibre
BookToPDF.tags=Libro,Cómic,Calibre,Convertir,manga,Amazon,Kindle
###########################
@@ -422,6 +450,8 @@ login.rememberme=Recordarme
login.invalid=Nombre de usuario o contraseña erróneos.
login.locked=Su cuenta se ha bloqueado.
login.signinTitle=Por favor, inicie sesión
login.ssoSignIn=Iniciar sesión a través del inicio de sesión único
login.oauth2AutoCreateDisabled=Usuario DE creación automática de OAUTH2 DESACTIVADO
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Convertir a página única
pageExtracter.title=Extraer Páginas
pageExtracter.header=Extraer Páginas
pageExtracter.submit=Extraer
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -642,17 +673,17 @@ compare.document.2=Documento 2
compare.submit=Comparar
#BookToPDF
BookToPDF.title=Books and Comics to PDF
BookToPDF.header=Book to PDF
BookToPDF.credit=Uses Calibre
BookToPDF.submit=Convert
BookToPDF.title=Libros y Cómics a PDF
BookToPDF.header=Libro a PDF
BookToPDF.credit=Usa Calibre
BookToPDF.submit=Convertir
#PDFToBook
PDFToBook.title=PDF to Book
PDFToBook.header=PDF to Book
PDFToBook.selectText.1=Format
PDFToBook.credit=Uses Calibre
PDFToBook.submit=Convert
PDFToBook.title=PDF a Libro
PDFToBook.header=PDF a Libro
PDFToBook.selectText.1=Formato
PDFToBook.credit=Utiliza Calibre
PDFToBook.submit=Convertir
#sign
sign.title=Firmar
@@ -673,6 +704,7 @@ repair.submit=Reparar
#flatten
flatten.title=Aplanar
flatten.header=Acoplar archivos PDF
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Aplanar
@@ -720,6 +752,7 @@ extractImages.submit=Extraer
fileToPDF.title=Archivo a PDF
fileToPDF.header=Convertir cualquier archivo a PDF
fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de archivos
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Los tipos de archivo soportados deben incluir los indicados a continuación; sin embargo, para una completa y acutualizada lista de formatos soportados, por favor consulte la documentación de LibreOffice
fileToPDF.submit=Convertir a PDF
@@ -756,11 +789,23 @@ merge.submit=Unir
pdfOrganiser.title=Organizador de páginas
pdfOrganiser.header=Organizador de páginas PDF
pdfOrganiser.submit=Organizar páginas
pdfOrganiser.mode=Modo
pdfOrganiser.mode.1=Orden de páginas personalizado
pdfOrganiser.mode.2=Orden inverso
pdfOrganiser.mode.3=Ordenar dúplex
pdfOrganiser.mode.4=Ordenar folleto
pdfOrganiser.mode.5=Orden de folleto de encuadernado lateral
pdfOrganiser.mode.6=División par-impar
pdfOrganiser.mode.7=Quitar primera
pdfOrganiser.mode.8=Quitar última
pdfOrganiser.mode.9=Quitar primera y última
pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1)
#multiTool
multiTool.title=Multi-herramienta PDF
multiTool.header=Multi-herramienta PDF
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=Ver PDF
@@ -771,6 +816,7 @@ pageRemover.title=Eliminador de páginas
pageRemover.header=Eliminador de páginas PDF
pageRemover.pagesToDelete=Páginas a eliminar (introducir una lista de números de página separados por coma):
pageRemover.submit=Eliminar Páginas
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Seleccionar ángulo de rotación (en múltiplos de 90 grados)
rotate.submit=Rotar
#merge
#split-pdfs
split.title=Dividir PDF
split.header=Dividir PDF
split.desc.1=Los números que seleccione son el número de página en el que desea hacer una división
split.desc.2=Como tal, seleccionar 1,3,7-8 dividiría un documento de 10 páginas en 6 archivos PDF separados con:
split.desc.2=Como tal, seleccionar 1,3,7-9 dividiría un documento de 10 páginas en 6 archivos PDF separados con:
split.desc.3=Documento #1: Página 1
split.desc.4=Documento #2: Páginas 2 y 3
split.desc.5=Documento #3: Páginas 4, 5 y 6
split.desc.6=Documento #4: Página 7
split.desc.7=Documento #5: Página 8
split.desc.8=Documento #6: Páginas 9 y 10
split.desc.5=Documento #3: Páginas 4, 5, 6 y 7
split.desc.6=Documento #4: Página 8
split.desc.7=Documento #5: Página 9
split.desc.8=Documento #6: Páginas 10
split.splitPages=Introducir las páginas para dividir:
split.submit=Dividir
@@ -858,6 +904,8 @@ watermark.selectText.7=Opacidad (0% - 100%):
watermark.selectText.8=Tipo de marca de agua:
watermark.selectText.9=Imagen de marca de agua:
watermark.submit=Añadir marca de agua
watermark.type.1=Texto
watermark.type.2=Imagen
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF a PDF/A
pdfToPDFA.header=PDF a PDF/A
pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A
pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Convertir
#PDFToHTML
PDFToHTML.title=PDF a HTML
PDFToHTML.header=PDF a HTML
PDFToHTML.credit=Este servicio utiliza LibreOffice para la conversión de archivos
PDFToHTML.credit=Este servicio utiliza pdftohtml para la conversión de archivos
PDFToHTML.submit=Convertir
@@ -990,7 +1040,16 @@ split-by-sections.vertical.label=Divisiones Verticales
split-by-sections.horizontal.placeholder=Introduzca el número de divisiones horizontales
split-by-sections.vertical.placeholder=Introduzca el número de divisiones verticales
split-by-sections.submit=Dividir PDF
split-by-sections.merge=Merge Into One PDF
split-by-sections.merge=Unir en Un PDF
#printFile
printFile.title=Imprimir archivo
printFile.header=Imprimir archivo en la impresora
printFile.selectText.1=Seleccionar archivo para imprimir
printFile.selectText.2=Introducir nombre de la impresora
printFile.submit=Imprimir
#licenses
licenses.nav=Licencias
@@ -1001,3 +1060,16 @@ licenses.version=Versión
licenses.license=Licencia
#error
error.sorry=¡Perdón por el fallo!
error.needHelp=Necesita ayuda / Encontró un fallo?
error.contactTip=Si sigue experimentando errores, no dude en contactarnos para solicitar soporte. Puede enviarnos un ticket en la página de GitHub o contactarnos mediante Discord:
error.404.head=404 - Página no encontrada | Ups, tropezamos con el código!
error.404.1=Parece que no podemos encontrar la página que está buscando.
error.404.2=Algo salió mal
error.github=Envíe un ticket en GitHub
error.showStack=Mostrar seguimiento de pila
error.copyStack=Mostrar seguimiento de pila
error.githubSubmit=GitHub - Enviar un ticket
error.discordSubmit=Discord - Enviar mensaje de soporte

View File

@@ -54,9 +54,25 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi
downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=Bihurtu
navbar.security=Segurtasuna
navbar.other=Beste bat
navbar.favorite=Favorites
navbar.darkmode=Modu iluna
navbar.pageOps=Orrialde-eragiketak
navbar.language=Languages
navbar.settings=Ezarpenak
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Ezarpenak
settings.update=Eguneratze eskuragarria
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=Aplikazioaren bertsioa:
settings.downloadOption.title=Hautatu deskargatzeko aukera (fitxategi bakarra deskargatzeko ZIP gabe):
settings.downloadOption.1=Ireki leiho berean
@@ -107,8 +132,9 @@ settings.downloadOption.3=Deskargatu fitxategia
settings.zipThreshold=ZIP fitxategiak deskargatutako fitxategi kopurua gainditzen denean
settings.signOut=Saioa itxi
settings.accountSettings=Kontuaren ezarpenak
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Change Credentials
changeCreds.header=Update Your Account Details
@@ -147,7 +173,7 @@ adminUserSettings.header=Admin Erabiltzailearen Ezarpenen Kontrolak
adminUserSettings.admin=Admin
adminUserSettings.user=Erabiltzaile
adminUserSettings.addUser=Erabiltzaile berria
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Rolak
adminUserSettings.role=Rol
adminUserSettings.actions=Ekintzak
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Gorde Erabiltzailea
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -422,6 +450,8 @@ login.rememberme=Oroitu nazazu
login.invalid=Okerreko erabiltzaile izena edo pasahitza.
login.locked=Zure kontua blokeatu egin da.
login.signinTitle=Mesedez, hasi saioa
login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez
login.oauth2AutoCreateDisabled=OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Orrialde bakarrera bihurtu
pageExtracter.title=Atera orriak
pageExtracter.header=Atera orriak
pageExtracter.submit=Atera
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -673,6 +704,7 @@ repair.submit=Konpondu
#flatten
flatten.title=Lautu
flatten.header=Akoplatu PDF fitxategiak
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Lautu
@@ -720,6 +752,7 @@ extractImages.submit=Atera
fileToPDF.title=Fitxategia PDF bihurtu
fileToPDF.header=Edozein fitxategi PDF bihurtu
fileToPDF.credit=Zerbitzu honek LibreOffice eta Unoconv erabiltzen ditu fitxategiak bihurtzeko
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Jasandako fitxategi-motek behekoak barne hartu behar dituzte; hala ere, jasandako formatuen zerrenda osoa eta eguneratua izateko, kontsultatu, mesedez, LibreOffice-en dokumentazioa
fileToPDF.submit=PDF bihurtu
@@ -756,11 +789,23 @@ merge.submit=Elkartu
pdfOrganiser.title=Orrialdeen antolatzailea
pdfOrganiser.header=PDF orrialdeen antolatzailea
pdfOrganiser.submit=Antolatu orrialdeak
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=PDF erabilera anitzeko tresna
multiTool.header=PDF erabilera anitzeko tresna
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=View PDF
@@ -771,6 +816,7 @@ pageRemover.title=Orrialdeen ezabatzailea
pageRemover.header=PDF orrialdeen ezabatzailea
pageRemover.pagesToDelete=Ezabatu beharreko orrialdeak (sartu komaz bereizitako orrialde-zenbakien zerrenda):
pageRemover.submit=Ezabatu orrialdeak
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Hautatu errotazio-angelua (90 graduko multiploetan):
rotate.submit=Biratu
#merge
#split-pdfs
split.title=Zatitu PDFa
split.header=Zatitu PDFa
split.desc.1=Hautatzen dituzun zenbakiak zatiketa egin nahi duzun orrialde-zenbakiak dira
split.desc.2=Beraz, 1,3,7-8 hautatzean 10 orrialdeko dokumentua zatituko luke 6 PDF fitxategi bereizituetan
split.desc.2=Beraz, 1,3,7-9 hautatzean 10 orrialdeko dokumentua zatituko luke 6 PDF fitxategi bereizituetan
split.desc.3=#1 Dokumentua: 1. orrialdea
split.desc.4=#2 Dokumentua: 2. eta 3. orrialdeak
split.desc.5=#3 Dokumentua: 4., 5. eta 6. orrialdeak
split.desc.6=#4 Dokumentua: 7. orrialdea
split.desc.7=#5 Dokumentua: 8. orrialdea
split.desc.8=#6 Dokumentua: 9. eta 10. orrialdeak
split.desc.5=#3 Dokumentua: 4., 5., 6. eta 7. orrialdeak
split.desc.6=#4 Dokumentua: 8. orrialdea
split.desc.7=#5 Dokumentua: 9. orrialdea
split.desc.8=#6 Dokumentua: 10. orrialdeak
split.splitPages=Sartu orrialdeak zatitzeko:
split.submit=Zatitu
@@ -858,6 +904,8 @@ watermark.selectText.7=Opakutasuna (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.submit=Gehitu ur-marka
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDFa PDF/A bihurtu
pdfToPDFA.header=PDFa PDF/A bihurtu
pdfToPDFA.credit=Zerbitzu honek OCRmyPDF erabiltzen du PDFak PDF/A bihurtzeko
pdfToPDFA.submit=Bihurtu
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Bihurtu
#PDFToHTML
PDFToHTML.title=PDFa HTML bihurtu
PDFToHTML.header=PDFa HTML bihurtu
PDFToHTML.credit=Zerbitzu honek LibreOffice erabiltzen du fitxategiak bihurtzeko
PDFToHTML.credit=Zerbitzu honek pdftohtml erabiltzen du fitxategiak bihurtzeko
PDFToHTML.submit=Bihurtu
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
split-by-sections.submit=Split PDF
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

View File

@@ -9,20 +9,20 @@ multiPdfPrompt=Sélectionnez les PDF
multiPdfDropPrompt=Sélectionnez (ou glissez-déposez) tous les PDF dont vous avez besoin
imgPrompt=Choisir une image
genericSubmit=Envoyer
processTimeWarning=Attention, ce processus peut prendre jusqu\u2019à une minute en fonction de la taille du fichier.
pageOrderPrompt=Ordre des pages (entrez une liste de numéros de page séparés par des virgules ou des fonctions telles que 2n+1)\u00a0:
pageSelectionPrompt=Sélection des pages (entrez une liste de numéros de page séparés par des virgules ou des fonctions telles que 2n+1)\u00a0:
processTimeWarning=Attention, ce processus peut prendre jusquà une minute en fonction de la taille du fichier.
pageOrderPrompt=Ordre des pages (entrez une liste de numéros de page séparés par des virgules ou des fonctions telles que 2n+1) :
pageSelectionPrompt=Sélection des pages (entrez une liste de numéros de page séparés par des virgules ou des fonctions telles que 2n+1) :
goToPage=Aller
true=Vrai
false=Faux
unknown=Inconnu
save=Enregistrer
saveToBrowser=Save to Browser
saveToBrowser=Enregistrer dans le navigateur
close=Fermer
filesSelected=fichiers sélectionnés
noFavourites=Aucun favori ajouté
downloadComplete=Téléchargement terminé
bored=Ennuyé d\u2019attendre\u00a0?
bored=Ennuyé dattendre ?
alphabet=Alphabet
downloadPdf=Télécharger le PDF
text=Texte
@@ -33,9 +33,9 @@ sizes.small=Petit
sizes.medium=Moyen
sizes.large=Grand
sizes.x-large=Très grand
error.pdfPassword=Le document PDF est protégé par un mot de passe et le mot de passe n\u2019a pas été fourni ou était incorrect
error.pdfPassword=Le document PDF est protégé par un mot de passe et le mot de passe na pas été fourni ou était incorrect
delete=Supprimer
username=Nom d\u2019utilisateur
username=Nom dutilisateur
password=Mot de passe
welcome=Bienvenue
property=Propriété
@@ -44,29 +44,47 @@ white=Blanc
red=Rouge
green=Vert
blue=Bleu
custom=Personnalisé\u2026
custom=Personnalisé
WorkInProgess=En cours de développement, merci de nous remonter les problèmes que vous pourriez constater!
poweredBy=Propulsé par
yes=Oui
no=Non
changedCredsMessage=Les identifiants ont été mis à jour\u00a0!
changedCredsMessage=Les identifiants ont été mis à jour !
notAuthenticatedMessage=Utilisateur non authentifié.
userNotFoundMessage=Utilisateur non trouvé.
incorrectPasswordMessage=Le mot de passe actuel est incorrect.
usernameExistsMessage=Le nouveau nom d\u2019utilisateur existe déjà.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
usernameExistsMessage=Le nouveau nom dutilisateur existe déjà.
invalidUsernameMessage=Nom dutilisateur invalide, le nom dutilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
deleteCurrentUserMessage=Impossible de supprimer lutilisateur actuellement connecté.
deleteUsernameExistsMessage=Le nom dutilisateur nexiste pas et ne peut pas être supprimé.
downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel.
downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché.
userAlreadyExistsOAuthMessage=L'utilisateur existe déjà en tant qu'utilisateur OAuth2.
userAlreadyExistsWebMessage=L'utilisateur existe déjà en tant qu'utilisateur Web.
error=Erreur
oops=Oups !
help=Aide
goHomepage=Aller à la page daccueil
joinDiscord=Rejoignez notre serveur Discord
seeDockerHub=Consulter le Docker Hub
visitGithub=Visiter le dépôt Github
donate=Faire un don
color=Couleur
sponsor=Sponsor
info=Info
###############
# Pipeline #
###############
pipeline.header=Menu Pipeline (Alpha)
pipeline.uploadButton=Télécharger une personnalisation
pipeline.uploadButton=Charger une personnalisation
pipeline.configureButton=Configurer
pipeline.defaultOption=Personnaliser
pipeline.submitButton=Soumettre
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,33 +105,41 @@ pipelineOptions.validateButton=Valider
#############
# NAVBAR #
#############
navbar.convert=Convertir
navbar.security=Sécurité
navbar.other=Autre
navbar.favorite=Favoris
navbar.darkmode=Mode sombre
navbar.pageOps=Opérations sur les pages
navbar.language=Langages
navbar.settings=Paramètres
navbar.allTools=OUtils
navbar.multiTool=Multi Outils
navbar.sections.organize=Oragnisation
navbar.sections.convertTo=Convertir en PDF
navbar.sections.convertFrom=Convertir depuis PDF
navbar.sections.security=Signalisation et sécurité
navbar.sections.advance=Mode avancé
navbar.sections.edit=Voir la modification
#############
# SETTINGS #
#############
settings.title=Paramètres
settings.update=Mise à jour disponible
settings.appVersion=Version de l\u2019application\u00a0:
settings.downloadOption.title=Choisissez l\u2019option de téléchargement (pour les téléchargements à fichier unique non ZIP)\u00a0:
settings.updateAvailable={0} est la version actuellement installée. Une nouvelle version ({1}) est disponible.
settings.appVersion=Version de lapplication :
settings.downloadOption.title=Choisissez loption de téléchargement (pour les téléchargements à fichier unique non ZIP) :
settings.downloadOption.1=Ouvrir dans la même fenêtre
settings.downloadOption.2=Ouvrir dans une nouvelle fenêtre
settings.downloadOption.3=Télécharger le fichier
settings.zipThreshold=Compresser les fichiers en ZIP lorsque le nombre de fichiers téléchargés dépasse
settings.signOut=Déconnexion
settings.accountSettings=Paramètres du compte
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Sauvegarder les entrées du formulaire
settings.cacheInputs.help=Permet de stocker les entrées précédemment utilisées pour les exécutions futures
changeCreds.title=Modifiez vos identifiants
changeCreds.header=Mettez à jour vos identifiants de connexion
changeCreds.changePassword=You are using default login credentials. Please enter a new password
changeCreds.newUsername=Nouveau nom d\u2019utilisateur
changeCreds.changePassword=Vous utilisez les identifiants de connexion par défaut. Veuillez saisir un nouveau mot de passe
changeCreds.newUsername=Nouveau nom dutilisateur
changeCreds.oldPassword=Mot de passe actuel
changeCreds.newPassword=Nouveau mot de passe
changeCreds.confirmNewPassword=Confirmer le nouveau mot de passe
@@ -123,10 +149,10 @@ changeCreds.submit=Soumettre les modifications
account.title=Paramètres du compte
account.accountSettings=Paramètres du compte
account.adminSettings=Paramètres d\u2019administration \u2013 Voir et ajouter des utilisateurs
account.adminSettings=Paramètres dadministration Voir et ajouter des utilisateurs
account.userControlSettings=Contrôle des paramètres des utilisateurs
account.changeUsername=Modifier le nom d\u2019utilisateur
account.newUsername=Nouveau nom d\u2019utilisateur
account.changeUsername=Modifier le nom dutilisateur
account.newUsername=Nouveau nom dutilisateur
account.password=Mot de passe de confirmation
account.oldPassword=Ancien mot de passe
account.newPassword=Nouveau mot de passe
@@ -138,8 +164,8 @@ account.syncTitle=Synchroniser les paramètres du navigateur avec le compte
account.settingsCompare=Comparaison des paramètres
account.property=Propriété
account.webBrowserSettings=Paramètres du navigateur
account.syncToBrowser=Synchroniser\u00a0: Compte → Navigateur
account.syncToAccount=Synchroniser\u00a0: Compte ← Navigateur
account.syncToBrowser=Synchroniser : Compte → Navigateur
account.syncToAccount=Synchroniser : Compte ← Navigateur
adminUserSettings.title=Administration des paramètres des utilisateurs
@@ -147,17 +173,19 @@ adminUserSettings.header=Administration des paramètres des utilisateurs
adminUserSettings.admin=Administateur
adminUserSettings.user=Utilisateur
adminUserSettings.addUser=Ajouter un utilisateur
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Le nom d'utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
adminUserSettings.roles=Rôles
adminUserSettings.role=Rôle
adminUserSettings.actions=Actions
adminUserSettings.apiUser=Utilisateur API limité
adminUserSettings.extraApiUser=Additional Limited API User
adminUserSettings.extraApiUser=Utilisateur limité supplémentaire de lAPI
adminUserSettings.webOnlyUser=Utilisateur Web uniquement
adminUserSettings.demoUser=Demo User (Paramètres par défaut)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Forcer l\u2019utilisateur à changer son nom d\u2019utilisateur/mot de passe lors de la connexion
adminUserSettings.forceChange=Forcer lutilisateur à changer son nom dutilisateur/mot de passe lors de la connexion
adminUserSettings.submit=Ajouter
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
adminUserSettings.authenticated=Authentifié
#############
# HOME-PAGE #
@@ -196,7 +224,7 @@ home.pdfToImage.desc=Convertissez un PDF en image (PNG, JPEG, GIF).
pdfToImage.tags=conversion,img,jpg,image,photo
home.pdfOrganiser.title=Organiser
home.pdfOrganiser.desc=Supprimez ou réorganisez les pages dans n\u2019importe quel ordre.
home.pdfOrganiser.desc=Supprimez ou réorganisez les pages dans nimporte quel ordre.
pdfOrganiser.tags=organiser,recto-verso,duplex,even,odd,sort,move
@@ -206,7 +234,7 @@ addImage.tags=img,jpg,image,photo
home.watermark.title=Ajouter un filigrane
home.watermark.desc=Ajoutez un filigrane personnalisé à votre PDF.
watermark.tags=texte,filigrane,label,propriété,droit d\u2019auteur,marque déposée,img,jpg,image,photo,copyright,trademark
watermark.tags=texte,filigrane,label,propriété,droit dauteur,marque déposée,img,jpg,image,photo,copyright,trademark
home.permissions.title=Modifier les permissions
home.permissions.desc=Modifiez les permissions de votre PDF.
@@ -235,16 +263,16 @@ home.changeMetadata.desc=Modifiez, supprimez ou ajoutez des métadonnées à un
changeMetadata.tags=métadonnées,titre,auteur,date,création,heure,éditeur,statistiques,title,author,date,creation,time,publisher,producer,stats,metadata
home.fileToPDF.title=Fichier en PDF
home.fileToPDF.desc=Convertissez presque n\u2019importe quel fichiers en PDF (DOCX, PNG, XLS, PPT, TXT et plus).
home.fileToPDF.desc=Convertissez presque nimporte quel fichiers en PDF (DOCX, PNG, XLS, PPT, TXT et plus).
fileToPDF.tags=convertion,transformation,format,document,image,slide,texte,conversion,office,docs,word,excel,powerpoint
home.ocr.title=OCR / Nettoyage des numérisations
home.ocr.desc=Utilisez l\u2019OCR pour analyser et détecter le texte des images d\u2019un PDF et le rajouter en temps que tel.
home.ocr.desc=Utilisez lOCR pour analyser et détecter le texte des images dun PDF et le rajouter en temps que tel.
ocr.tags=ocr,reconnaissance,texte,image,numérisation,scan,read,identify,detection,editable
home.extractImages.title=Extraire les images
home.extractImages.desc=Extrayez toutes les images d\u2019un PDF et enregistrez-les dans un ZIP.
home.extractImages.desc=Extrayez toutes les images dun PDF et enregistrez-les dans un ZIP.
extractImages.tags=image,photo,save,archive,zip,capture,grab
home.pdfToPDFA.title=PDF en PDF/A
@@ -273,7 +301,7 @@ home.PDFToXML.desc=Convertissez un PDF au format XML.
PDFToXML.tags=xml,extraction de données,contenu structuré,interopérabilité,data-extraction,structured-content,interop,transformation,convert
home.ScannerImageSplit.title=Diviser les photos numérisées
home.ScannerImageSplit.desc=Divisez plusieurs photos à partir d\u2019une photo ou d\u2019un PDF.
home.ScannerImageSplit.desc=Divisez plusieurs photos à partir dune photo ou dun PDF.
ScannerImageSplit.tags=diviser,détecter automatiquement,numériser,separate,auto-detect,scans,multi-photo,organize
home.sign.title=Signer
@@ -281,7 +309,7 @@ home.sign.desc=Ajoutez une signature au PDF avec un dessin, du texte ou une imag
sign.tags=signer,authorize,initials,drawn-signature,text-sign,image-signature
home.flatten.title=Rendre inerte
home.flatten.desc=Supprimez tous les éléments et formulaires interactifs d\u2019un PDF.
home.flatten.desc=Supprimez tous les éléments et formulaires interactifs dun PDF.
flatten.tags=inerte,static,deactivate,non-interactive,streamline
home.repair.title=Réparer
@@ -289,11 +317,11 @@ home.repair.desc=Essayez de réparer un PDF corrompu ou cassé.
repair.tags=réparer,restaurer,corriger,récupérer,fix,restore,correction,recover
home.removeBlanks.title=Supprimer les pages vierges
home.removeBlanks.desc=Détectez et supprimez les pages vierges d\u2019un PDF.
home.removeBlanks.desc=Détectez et supprimez les pages vierges dun PDF.
removeBlanks.tags=pages vierges,supprimer,nettoyer,cleanup,streamline,non-content,organize
home.removeAnnotations.title=Supprimer les annotations
home.removeAnnotations.desc=Supprimer tous les commentaires/annotations d\u2019un PDF.
home.removeAnnotations.desc=Supprimer tous les commentaires/annotations dun PDF.
removeAnnotations.tags=commentaires,supprimer,annotations,highlight,notes,markup,remove
home.compare.title=Comparer
@@ -305,11 +333,11 @@ home.certSign.desc=Signez un PDF avec un certificat ou une clé (PEM/P12).
certSign.tags=signer,chiffrer,certificat,authenticate,PEM,P12,official,encrypt
home.pageLayout.title=Fusionner des pages
home.pageLayout.desc=Fusionnez plusieurs pages d\u2019un PDF en une seule.
home.pageLayout.desc=Fusionnez plusieurs pages dun PDF en une seule.
pageLayout.tags=fusionner,merge,composite,single-view,organize
home.scalePages.title=Ajuster l\u2019échelle ou la taille
home.scalePages.desc=Modifiez la taille ou l\u2019échelle d\u2019une page et/ou de son contenu.
home.scalePages.title=Ajuster léchelle ou la taille
home.scalePages.desc=Modifiez la taille ou léchelle dune page et/ou de son contenu.
scalePages.tags=ajuster,redimensionner,resize,modify,dimension,adapt
home.pipeline.title=Pipeline (avancé)
@@ -325,11 +353,11 @@ home.auto-rename.desc=Renommez automatiquement un fichier PDF en fonction de son
auto-rename.tags=renommer,détection automatique,réétiqueter,auto-detect,header-based,organize,relabel
home.adjust-contrast.title=Ajuster les couleurs
home.adjust-contrast.desc=Ajustez le contraste, la saturation et la luminosité d\u2019un PDF.
home.adjust-contrast.desc=Ajustez le contraste, la saturation et la luminosité dun PDF.
adjust-contrast.tags=ajuster,couleurs,amélioration,color-correction,tune,modify,enhance
home.crop.title=Redimensionner
home.crop.desc=Redimmensionnez un PDF pour réduire sa taille (en conservant le texte\u00a0!).
home.crop.desc=Redimmensionnez un PDF pour réduire sa taille (en conservant le texte !).
crop.tags=redimensionner,trim,shrink,edit,shape
home.autoSplitPDF.title=Séparer automatiquement les pages
@@ -341,16 +369,16 @@ home.sanitizePdf.desc=Supprimez les scripts et autres éléments des PDF.
sanitizePdf.tags=assainir,sécurisé,clean,secure,safe,remove-threats
home.URLToPDF.title=URL en PDF
home.URLToPDF.desc=Convertissez n\u2019importe quelle URL http(s) en PDF.
home.URLToPDF.desc=Convertissez nimporte quelle URL http(s) en PDF.
URLToPDF.tags=pdf,contenu Web,save-page,web-to-doc,archive
home.HTMLToPDF.title=HTML en PDF
home.HTMLToPDF.desc=Convertissez n\u2019importe quel fichier HTML ou ZIP en PDF.
home.HTMLToPDF.desc=Convertissez nimporte quel fichier HTML ou ZIP en PDF.
HTMLToPDF.tags=html,markup,contenu Web,transformation,convert
home.MarkdownToPDF.title=Markdown en PDF
home.MarkdownToPDF.desc=Convertissez n\u2019importe quel fichier Markdown en PDF.
home.MarkdownToPDF.desc=Convertissez nimporte quel fichier Markdown en PDF.
MarkdownToPDF.tags=markdown,markup,contenu Web,transformation,convert
@@ -374,11 +402,11 @@ home.showJS.desc=Recherche et affiche tout JavaScript injecté dans un PDF.
showJS.tags=JS
home.autoRedact.title=Caviarder automatiquement
home.autoRedact.desc=Caviardez automatiquement les informations sensibles d\u2019un PDF.
home.autoRedact.desc=Caviardez automatiquement les informations sensibles dun PDF.
autoRedact.tags=caviarder,redact,auto
home.tableExtraxt.title=PDF en CSV
home.tableExtraxt.desc=Extrait les tableaux d\u2019un PDF et les transforme en CSV.
home.tableExtraxt.desc=Extrait les tableaux dun PDF et les transforme en CSV.
tableExtraxt.tags=CSV,Table Extraction,extract,convert
@@ -388,24 +416,24 @@ autoSizeSplitPDF.tags=pdf,split,document,organization
home.overlay-pdfs.title=Incrustation de PDF
home.overlay-pdfs.desc=Incrustation d\u2019un PDF sur un autre PDF.
home.overlay-pdfs.desc=Incrustation dun PDF sur un autre PDF.
overlay-pdfs.tags=Overlay,incrustation
home.split-by-sections.title=Séparer un PDF en sections
home.split-by-sections.desc=Diviser chaque page d\u2019un PDF en sections horizontales/verticales plus petites.
home.split-by-sections.desc=Diviser chaque page dun PDF en sections horizontales/verticales plus petites.
split-by-sections.tags=Sections,Diviser,Section Split, Divide, Customize
home.AddStampRequest.title=Ajouter un tampon sur un PDF
home.AddStampRequest.desc=Ajouter un texte ou l\u2019image d\u2019un tampon à un emplacement défini.
home.AddStampRequest.desc=Ajouter un texte ou limage dun tampon à un emplacement défini.
AddStampRequest.tags=Tampon,Ajouter,Stamp,Add image,center image,Watermark,PDF,Embed,Customize
home.PDFToBook.title=PDF to Book
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
home.PDFToBook.desc=Convertit le PDF en formats livre/bande dessinée à l'aide de calibre
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
home.BookToPDF.desc=Convertit les formats de livres/bandes dessinées en PDF à l'aide de calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
@@ -419,9 +447,11 @@ login.title=Connexion
login.header=Connexion
login.signin=Connexion
login.rememberme=Se souvenir de moi
login.invalid=Nom d\u2019utilisateur ou mot de passe invalide.
login.invalid=Nom dutilisateur ou mot de passe invalide.
login.locked=Votre compte a été verrouillé.
login.signinTitle=Veuillez vous connecter
login.ssoSignIn=Se connecter via l'authentification unique
login.oauth2AutoCreateDisabled=OAUTH2 Création automatique d'utilisateur désactivée
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Convertir en une seule page
pageExtracter.title=Extraire des pages
pageExtracter.header=Extraire des pages
pageExtracter.submit=Extraire
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -485,15 +516,15 @@ HTMLToPDF.header=HTML en PDF
HTMLToPDF.help=Accepte les fichiers HTML et les ZIP contenant du HTML, du CSS, des images, etc. (requis).
HTMLToPDF.submit=Convertir
HTMLToPDF.credit=Utilise WeasyPrint.
HTMLToPDF.zoom=Niveau de zoom pour l\u2019affichage du site web.
HTMLToPDF.zoom=Niveau de zoom pour laffichage du site web.
HTMLToPDF.pageWidth=Largeur de la page en centimètres. (Vide par défaut)
HTMLToPDF.pageHeight=Hauteur de la page en centimètres. (Vide par défaut)
HTMLToPDF.marginTop=Marge supérieure de la page en millimètres. (Vide par défaut)
HTMLToPDF.marginBottom=Marge inférieure de la page en millimètres. (Vide par défaut)
HTMLToPDF.marginLeft=Marge gauche de la page en millimètres. (Vide par défaut)
HTMLToPDF.marginRight=Marge droite de la page en millimètres. (Vide par défaut)
HTMLToPDF.printBackground=Restituer l\u2019image de fond des sites web.
HTMLToPDF.defaultHeader=Activer l\u2019entête par défaut (Nom et numéro de page)
HTMLToPDF.printBackground=Restituer limage de fond des sites web.
HTMLToPDF.defaultHeader=Activer lentête par défaut (Nom et numéro de page)
HTMLToPDF.cssMediaType=Modifier le type de média CSS de la page.
HTMLToPDF.none=Aucun
HTMLToPDF.print=Imprimer
@@ -571,9 +602,9 @@ autoSplitPDF.header=Séparer automatiquement les pages
autoSplitPDF.description=Imprimez, insérez, numérisez, téléchargez et laissez-nous séparer automatiquement vos documents. Aucun travail de tri manuel nécessaire.
autoSplitPDF.selectText.1=Imprimez des feuilles de séparation ci-dessous (le mode noir et blanc convient).
autoSplitPDF.selectText.2=Numérisez tous vos documents en une seule fois en insérant les feuilles intercalaires entre eux.
autoSplitPDF.selectText.3=Téléchargez le fichier PDF numérisé et laissez Stirling PDF s\u2019occuper du reste.
autoSplitPDF.selectText.3=Téléchargez le fichier PDF numérisé et laissez Stirling PDF soccuper du reste.
autoSplitPDF.selectText.4=Les feuilles de séparation sont automatiquement détectées et supprimées, garantissant un document final soigné.
autoSplitPDF.formPrompt=PDF contenant des feuilles de séparation de Stirling PDF\u00a0:
autoSplitPDF.formPrompt=PDF contenant des feuilles de séparation de Stirling PDF :
autoSplitPDF.duplexMode=Mode recto-verso
autoSplitPDF.dividerDownload1=Auto Splitter Divider (minimal).pdf
autoSplitPDF.dividerDownload2=Auto Splitter Divider (with instructions).pdf
@@ -593,10 +624,10 @@ pageLayout.submit=Fusionner
#scalePages
scalePages.title=Ajuster la taille ou l\u2019échelle
scalePages.header=Ajuster la taille ou l\u2019échelle
scalePages.pageSize=Taille d\u2019une page du document
scalePages.scaleFactor=Niveau de zoom (recadrage) d\u2019une page
scalePages.title=Ajuster la taille ou léchelle
scalePages.header=Ajuster la taille ou léchelle
scalePages.pageSize=Taille dune page du document
scalePages.scaleFactor=Niveau de zoom (recadrage) dune page
scalePages.submit=Ajuster
@@ -604,10 +635,10 @@ scalePages.submit=Ajuster
certSign.title=Signer avec un certificat
certSign.header=Signer avec un certificat (Travail en cours)
certSign.selectPDF=PDF à signer
certSign.jksNote=Note: Si votre type de certificat n\u2019est pas listé ci\u002Ddessous, merci de le convertir en fichier Java Keystore (.jks) en utilisant l\u2019outil en ligne de commande keytool. Puis choisissez l\u2019option Fichier .jks ci\u002Ddessous.
certSign.jksNote=Note: Si votre type de certificat nest pas listé ci-dessous, merci de le convertir en fichier Java Keystore (.jks) en utilisant loutil en ligne de commande keytool. Puis choisissez loption Fichier .jks ci-dessous.
certSign.selectKey=Fichier de clé privée (format PKCS#8, peut être .pem ou .der)
certSign.selectCert=Fichier de certificat (format X.509, peut être .pem ou .der)
certSign.selectP12=Fichier keystore de clés PKCS#12 (.p12 ou .pfx) (facultatif, s\u2019il n\u2019est fourni, il doit contenir votre clé privée et votre certificat)
certSign.selectP12=Fichier keystore de clés PKCS#12 (.p12 ou .pfx) (facultatif, sil nest fourni, il doit contenir votre clé privée et votre certificat)
certSign.selectJKS=Sélectionner votre fichier Java Keystore File (.jks or .keystore):
certSign.certType=Type de certificat
certSign.password=Mot de passe keystore ou clé privée le cas échéant
@@ -622,7 +653,7 @@ certSign.submit=Signer
removeBlanks.title=Supprimer les pages vierges
removeBlanks.header=Supprimer les pages vierges
removeBlanks.threshold=Seuil de blancheur des pixels
removeBlanks.thresholdDesc=Seuil pour déterminer à quel point un pixel blanc doit être blanc pour être classé comme «\u00a0blanc\u00a0» (0 = noir, 255 = blanc pur).
removeBlanks.thresholdDesc=Seuil pour déterminer à quel point un pixel blanc doit être blanc pour être classé comme « blanc » (0 = noir, 255 = blanc pur).
removeBlanks.whitePercent=Pourcentage de blanc
removeBlanks.whitePercentDesc=Pourcentage de la page qui doit contenir des pixels « blancs » à supprimer.
removeBlanks.submit=Supprimer les pages vierges
@@ -644,15 +675,15 @@ compare.submit=Comparer
#BookToPDF
BookToPDF.title=Books and Comics to PDF
BookToPDF.header=Book to PDF
BookToPDF.credit=Uses Calibre
BookToPDF.submit=Convert
BookToPDF.credit=Utiliser Calibre
BookToPDF.submit=Convertir
#PDFToBook
PDFToBook.title=PDF to Book
PDFToBook.header=PDF to Book
PDFToBook.selectText.1=Format
PDFToBook.credit=Uses Calibre
PDFToBook.submit=Convert
PDFToBook.credit=Utiliser Calibre
PDFToBook.submit=Convertir
#sign
sign.title=Signer
@@ -673,20 +704,21 @@ repair.submit=Réparer
#flatten
flatten.title=Rendre inerte
flatten.header=Rendre inerte
flatten.flattenOnlyForms=Aplatir uniquement les formulaires
flatten.submit=Rendre inerte
#ScannerImageSplit
ScannerImageSplit.selectText.1=Seuil de rotation
ScannerImageSplit.selectText.2=Définit l\u2019angle absolu minimum requis pour la rotation de l\u2019image (par défaut\u00a0: 10).
ScannerImageSplit.selectText.2=Définit langle absolu minimum requis pour la rotation de limage (par défaut : 10).
ScannerImageSplit.selectText.3=Tolérance
ScannerImageSplit.selectText.4=Détermine la plage de variation de couleur autour de la couleur d\u2019arrière-plan estimée (par défaut\u00a0: 20).
ScannerImageSplit.selectText.4=Détermine la plage de variation de couleur autour de la couleur darrière-plan estimée (par défaut : 20).
ScannerImageSplit.selectText.5=Surface minimale
ScannerImageSplit.selectText.6=Définit la surface minimale pour une photo (par défaut\u00a0: 8\u202f000).
ScannerImageSplit.selectText.6=Définit la surface minimale pour une photo (par défaut : 8000).
ScannerImageSplit.selectText.7=Surface de contour minimale
ScannerImageSplit.selectText.8=Définit la surface de contour minimale pour une photo (par défaut\u00a0: 500).
ScannerImageSplit.selectText.8=Définit la surface de contour minimale pour une photo (par défaut : 500).
ScannerImageSplit.selectText.9=Taille de la bordure
ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut\u00a0: 1).
ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut : 1).
#OCR
@@ -695,24 +727,24 @@ ocr.header=OCR (Reconnaissance optique de caractères) / Nettoyage des numérisa
ocr.selectText.1=Langues à détecter dans le PDF (celles listées sont celles actuellement détectées)
ocr.selectText.2=Produire un fichier texte contenant le texte détecté à côté du PDF
ocr.selectText.3=Corriger les pages qui ont été numérisées à un angle oblique en les remettant en place
ocr.selectText.4=Nettoyer la page afin qu\u2019il soit moins probable que l\u2019OCR trouve du texte dans le bruit de fond, sans modifier la sortie
ocr.selectText.5=Nettoyer la page afin qu\u2019il soit moins probable que l\u2019OCR trouve du texte dans le bruit de fond, en modifiant la sortie
ocr.selectText.6=Ignorer les pages contenant du texte interactif, n\u2019analyser que les pages qui sont des images
ocr.selectText.7=Forcer l\u2019OCR, analyser chaque page et supprimer tous les éléments de texte d\u2019origine
ocr.selectText.4=Nettoyer la page afin quil soit moins probable que lOCR trouve du texte dans le bruit de fond, sans modifier la sortie
ocr.selectText.5=Nettoyer la page afin quil soit moins probable que lOCR trouve du texte dans le bruit de fond, en modifiant la sortie
ocr.selectText.6=Ignorer les pages contenant du texte interactif, nanalyser que les pages qui sont des images
ocr.selectText.7=Forcer lOCR, analyser chaque page et supprimer tous les éléments de texte dorigine
ocr.selectText.8=Normal (génère une erreur si le PDF contient du texte)
ocr.selectText.9=Paramètres additionnels
ocr.selectText.10=Mode OCR
ocr.selectText.11=Supprimer les images après l\u2019OCR (Supprime TOUTES les images, utile uniquement si elles font partie de l\u2019étape de conversion)
ocr.selectText.11=Supprimer les images après lOCR (Supprime TOUTES les images, utile uniquement si elles font partie de létape de conversion)
ocr.selectText.12=Type de rendu (avancé)
ocr.help=Veuillez lire cette documentation pour savoir comment utiliser l\u2019OCR pour d\u2019autres langues ou une utilisation hors Docker\u00a0:
ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l\u2019OCR.
ocr.help=Veuillez lire cette documentation pour savoir comment utiliser lOCR pour dautres langues ou une utilisation hors Docker :
ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour lOCR.
ocr.submit=Traiter
#extractImages
extractImages.title=Extraire les images
extractImages.header=Extraire les images
extractImages.selectText=Format d\u2019image dans lequel convertir les images extraites
extractImages.selectText=Format dimage dans lequel convertir les images extraites
extractImages.submit=Extraire
@@ -720,26 +752,27 @@ extractImages.submit=Extraire
fileToPDF.title=Fichier en PDF
fileToPDF.header=Convertir un fichier en PDF
fileToPDF.credit=Ce service utilise LibreOffice et Unoconv pour la conversion de fichiers.
fileToPDF.supportedFileTypesInfo=Types de fichiers pris en charge
fileToPDF.supportedFileTypes=Les types de fichiers pris en charge doivent inclure les éléments ci-dessous, mais pour une liste complète et mise à jour des formats pris en charge, veuillez vous reporter à la documentation de LibreOffice.
fileToPDF.submit=Convertir
#compress
compress.title=Compresser un PDF
compress.header=Compresser un PDF (lorsque c\u2019est possible!)
compress.credit=Ce service utilise Ghostscript pour la compression et l\u2019optimisation des PDF.
compress.selectText.1=Mode manuel \u2013 de 1 à 4
compress.selectText.2=Niveau d\u2019optimisation
compress.header=Compresser un PDF (lorsque cest possible!)
compress.credit=Ce service utilise Ghostscript pour la compression et loptimisation des PDF.
compress.selectText.1=Mode manuel de 1 à 4
compress.selectText.2=Niveau doptimisation
compress.selectText.3=4 (terrible pour les images textuelles)
compress.selectText.4=Mode automatique \u2013 ajuste automatiquement la qualité pour obtenir le PDF à la taille exacte
compress.selectText.5=Taille PDF attendue (par exemple, 25\u202fMB, 10,8\u202fMB, 25\u202fKB)
compress.selectText.4=Mode automatique ajuste automatiquement la qualité pour obtenir le PDF à la taille exacte
compress.selectText.5=Taille PDF attendue (par exemple, 25MB, 10,8MB, 25KB)
compress.submit=Compresser
#Add image
addImage.title=Ajouter une image
addImage.header=Ajouter une image
addImage.everyPage=Toutes les pages\u00a0?
addImage.everyPage=Toutes les pages ?
addImage.upload=Télécharger une image
addImage.submit=Ajouter une image
@@ -756,11 +789,23 @@ merge.submit=Fusionner
pdfOrganiser.title=Organiser
pdfOrganiser.header=Organiser les pages
pdfOrganiser.submit=Organiser
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Ordre des pages personnalisé
pdfOrganiser.mode.2=Ordre inverse
pdfOrganiser.mode.3=Tri recto verso
pdfOrganiser.mode.4=Tri des livrets
pdfOrganiser.mode.5=Tri de livrets à points latéraux
pdfOrganiser.mode.6=Partage impair-pair
pdfOrganiser.mode.7=Supprimer le premier
pdfOrganiser.mode.8=Supprimer le dernier
pdfOrganiser.mode.9=Supprimer le premier et le dernier
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=Outil multifonction PDF
multiTool.header=Outil multifonction PDF
multiTool.uploadPrompts=Nom du fichier
#view pdf
viewPdf.title=Visualiser un PDF
@@ -769,28 +814,29 @@ viewPdf.header=Visualiser un PDF
#pageRemover
pageRemover.title=Supprimer des pages
pageRemover.header=Supprimer des pages
pageRemover.pagesToDelete=Pages à supprimer (entrez une liste de numéros de pages séparés par des virgules)\u00a0:
pageRemover.pagesToDelete=Pages à supprimer (entrez une liste de numéros de pages séparés par des virgules) :
pageRemover.submit=Supprimer les pages
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
rotate.title=Pivoter
rotate.header=Pivoter
rotate.selectAngle=Angle de rotation (par multiples de 90\u202fdegrés)
rotate.selectAngle=Angle de rotation (par multiples de 90degrés)
rotate.submit=Pivoter
#merge
#split-pdfs
split.title=Diviser
split.header=Diviser
split.desc.1=Les numéros que vous sélectionnez sont le numéro de page sur lequel vous souhaitez faire une division
split.desc.2=Ainsi, la sélection de 1,3,7-8 diviserait un document de 10 pages en 6 PDF distincts avec\u00a0:
split.desc.2=Ainsi, la sélection de 1,3,7-9 diviserait un document de 10 pages en 6 PDF distincts avec :
split.desc.3=Document #1: Page 1
split.desc.4=Document #2: Page 2 et 3
split.desc.5=Document #3: Page 4, 5 et 6
split.desc.6=Document #4: Page 7
split.desc.7=Document #5: Page 8
split.desc.8=Document #6: Page 9 et 10
split.desc.5=Document #3: Page 4, 5, 6 et 7
split.desc.6=Document #4: Page 8
split.desc.7=Document #5: Page 9
split.desc.8=Document #6: Page 10
split.splitPages=Pages sur lesquelles diviser
split.submit=Diviser
@@ -799,9 +845,9 @@ split.submit=Diviser
imageToPDF.title=Image en PDF
imageToPDF.header=Image en PDF
imageToPDF.submit=Convertir
imageToPDF.selectLabel=Options d\u2019ajustement de l\u2019image
imageToPDF.selectLabel=Options dajustement de limage
imageToPDF.fillPage=Remplir la page
imageToPDF.fitDocumentToImage=Ajuster la page à l\u2019image
imageToPDF.fitDocumentToImage=Ajuster la page à limage
imageToPDF.maintainAspectRatio=Maintenir les proportions
imageToPDF.selectText.2=Rotation automatique du PDF
imageToPDF.selectText.3=Logique multi-fichiers (uniquement activée si vous travaillez avec plusieurs images)
@@ -812,14 +858,14 @@ imageToPDF.selectText.5=Convertir en PDF séparés
#pdfToImage
pdfToImage.title=Image en PDF
pdfToImage.header=Image en PDF
pdfToImage.selectText=Format d\u2019image
pdfToImage.selectText=Format dimage
pdfToImage.singleOrMultiple=Type de résultat
pdfToImage.single=Une seule grande image
pdfToImage.multi=Plusieurs images
pdfToImage.colorType=Type d\u2019impression
pdfToImage.colorType=Type dimpression
pdfToImage.color=Couleur
pdfToImage.grey=Niveaux de gris
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données\u00a0!)
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !)
pdfToImage.submit=Convertir
@@ -827,21 +873,21 @@ pdfToImage.submit=Convertir
addPassword.title=Ajouter un mot de passe
addPassword.header=Ajouter un mot de passe
addPassword.selectText.1=PDF à chiffrer
addPassword.selectText.2=Mot de passe de l\u2019utilisateur
addPassword.selectText.2=Mot de passe de lutilisateur
addPassword.selectText.3=Longueur de la clé de chiffrement
addPassword.selectText.4=Les valeurs plus élevées sont plus fortes, mais les valeurs plus faibles ont une meilleure compatibilité.
addPassword.selectText.5=Autorisations à définir (utilisation recommandée avec le mot de passe du propriétaire)
addPassword.selectText.6=Empêcher l\u2019assemblage du document
addPassword.selectText.7=Empêcher l\u2019extraction de contenu
addPassword.selectText.8=Empêcher l\u2019extraction pour l\u2019accessibilité
addPassword.selectText.6=Empêcher lassemblage du document
addPassword.selectText.7=Empêcher lextraction de contenu
addPassword.selectText.8=Empêcher lextraction pour laccessibilité
addPassword.selectText.9=Empêcher de remplir les formulaires
addPassword.selectText.10=Empêcher la modification
addPassword.selectText.11=Empêcher la modification des annotations
addPassword.selectText.12=Empêcher l\u2019impression
addPassword.selectText.13=Empêcher l\u2019impression des différents formats
addPassword.selectText.12=Empêcher limpression
addPassword.selectText.13=Empêcher limpression des différents formats
addPassword.selectText.14=Mot de passe du propriétaire
addPassword.selectText.15=Restreint ce qui peut être fait avec le document une fois qu\u2019il est ouvert (non pris en charge par tous les lecteurs).
addPassword.selectText.16=Restreint l\u2019ouverture du document lui-même.
addPassword.selectText.15=Restreint ce qui peut être fait avec le document une fois quil est ouvert (non pris en charge par tous les lecteurs).
addPassword.selectText.16=Restreint louverture du document lui-même.
addPassword.submit=Chiffrer
@@ -858,6 +904,8 @@ watermark.selectText.7=Opacité (de 0% à 100%)
watermark.selectText.8=Type de filigrane
watermark.selectText.9=Image du filigrane
watermark.submit=Ajouter un filigrane
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -866,14 +914,14 @@ permissions.header=Modifier les permissions
permissions.warning=Attention, pour que ces permissions soient immuables il est recommandé de les paramétrer avec un mot de passe via la page Ajouter un mot de passe.
permissions.selectText.1=Sélectionnez le PDF
permissions.selectText.2=Permissions à définir
permissions.selectText.3=Empêcher l\u2019assemblage du document
permissions.selectText.4=Empêcher l\u2019extraction de contenu
permissions.selectText.5=Empêcher l\u2019extraction pour l\u2019accessibilité
permissions.selectText.3=Empêcher lassemblage du document
permissions.selectText.4=Empêcher lextraction de contenu
permissions.selectText.5=Empêcher lextraction pour laccessibilité
permissions.selectText.6=Empêcher de remplir les formulaires
permissions.selectText.7=Empêcher la modification
permissions.selectText.8=Empêcher la modification des annotations
permissions.selectText.9=Empêcher l\u2019impression
permissions.selectText.10=Empêcher l\u2019impression des différents formats
permissions.selectText.9=Empêcher limpression
permissions.selectText.10=Empêcher limpression des différents formats
permissions.submit=Modifier
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF en PDF/A
pdfToPDFA.header=PDF en PDF/A
pdfToPDFA.credit=Ce service utilise OCRmyPDF pour la conversion en PDF/A.
pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Ne fonctionne actuellement pas pour plusieurs entrées à la fois
pdfToPDFA.outputFormat=Format de sortie
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Convertir
#PDFToHTML
PDFToHTML.title=PDF en HTML
PDFToHTML.header=PDF en HTML
PDFToHTML.credit=Ce service utilise LibreOffice pour la conversion de fichiers.
PDFToHTML.credit=Ce service utilise pdftohtml pour la conversion de fichiers.
PDFToHTML.submit=Convertir
@@ -970,13 +1020,13 @@ split-by-size-or-count.submit=Séparer
overlay-pdfs.header=Incrustation de PDF
overlay-pdfs.baseFile.label=Sélectionner le fichier PDF de base
overlay-pdfs.overlayFiles.label=Sélectionner les fichiers PDF à superposer
overlay-pdfs.mode.label=Sélectionner le mode d\u2019incrustation
overlay-pdfs.mode.label=Sélectionner le mode dincrustation
overlay-pdfs.mode.sequential=Superposition séquentielle
overlay-pdfs.mode.interleaved=Superposition entrelacée
overlay-pdfs.mode.fixedRepeat=Superposition à répétition fixe
overlay-pdfs.counts.label=Nombre de superpositions (pour le mode de répétition fixe)
overlay-pdfs.counts.placeholder=Compteurs (séparés par des virgules, exemple : 2,3,1)
overlay-pdfs.position.label=Définir la position de l\u2019incrustation
overlay-pdfs.position.label=Définir la position de lincrustation
overlay-pdfs.position.foreground=Premier plan
overlay-pdfs.position.background=Arrière-plan
overlay-pdfs.submit=Soumettre
@@ -990,7 +1040,16 @@ split-by-sections.vertical.label=Divisions verticales
split-by-sections.horizontal.placeholder=Saisir le nombre de divisions horizontales
split-by-sections.vertical.placeholder=Entrer le nombre de divisions verticales
split-by-sections.submit=Diviser le PDF
split-by-sections.merge=Merge Into One PDF
split-by-sections.merge=Fusionner en un seul PDF
#printFile
printFile.title=Imprimer le fichier
printFile.header=Imprimer le fichier sur l'imprimante
printFile.selectText.1=Sélectionner le fichier à imprimer
printFile.selectText.2=Entrez le nom de l'imprimante
printFile.submit=Imprimer
#licenses
licenses.nav=Licences
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=Licence
#error
error.sorry=Désolé pour ce problème !
error.needHelp=Besoin daide / Vous avez trouvé un problème ?
error.contactTip=Si vous avez encore des problèmes, nhésitez pas à nous contacter pour obtenir de laide. Vous pouvez soumettre un ticket sur notre page GitHub ou nous contacter via Discord :
error.404.head=404 - Page non trouvée | oups on sest foiré !
error.404.1=Nous ne parvenons pas à trouver la page que vous recherchez.
error.404.2=Quelque chose na pas fonctionné
error.github=Créer un ticket sur GitHub
error.showStack=Afficher la Stack Trace
error.copyStack=Copier la Stack Trace
error.githubSubmit=GitHub - Créer un ticket
error.discordSubmit=Discord - Poster un message de demande dassistance

View File

@@ -54,9 +54,25 @@ notAuthenticatedMessage=उपयोगकर्ता प्रमाणित
userNotFoundMessage=उपयोगकर्ता नहीं मिला।
incorrectPasswordMessage=वर्तमान पासवर्ड गलत है।
usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है।
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=कनवर्ट
navbar.security=सुरक्षा
navbar.other=विविध
navbar.favorite=Favorites
navbar.darkmode=डार्क मोड
navbar.pageOps=पेज कार्य
navbar.language=Languages
navbar.settings=सेटिंग्स
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=सेटिंग्स
settings.update=अपडेट उपलब्ध है
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=ऐप संस्करण:
settings.downloadOption.title=डाउनलोड विकल्प चुनें (एकल फ़ाइल गैर-ज़िप डाउनलोड के लिए):
settings.downloadOption.1=एक ही विंडो में खोलें
@@ -107,8 +132,9 @@ settings.downloadOption.3=फ़ाइल डाउनलोड करें
settings.zipThreshold=जब डाउनलोड की गई फ़ाइलों की संख्या सीमा से अधिक हो
settings.signOut=साइन आउट
settings.accountSettings=खाता सेटिंग्स
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=क्रेडेंशियल बदलें
changeCreds.header=अपना खाता विवरण अपडेट करें
@@ -147,7 +173,7 @@ adminUserSettings.header=व्यवस्थापक उपयोगकर्
adminUserSettings.admin=व्यवस्थापक
adminUserSettings.user=उपयोगकर्ता
adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=रोल्स
adminUserSettings.role=रोल
adminUserSettings.actions=क्रियाएँ
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें
adminUserSettings.submit=उपयोगकर्ता को सहेजें
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -422,6 +450,8 @@ login.rememberme=मुझे याद रखें
login.invalid=अमान्य उपयोगकर्ता नाम या पासवर्ड।
login.locked=आपका खाता लॉक कर दिया गया है।
login.signinTitle=कृपया साइन इन करें
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
login.oauth2AutoCreateDisabled=OAUTH2 ऑटो - क्रिएट यूज़र अक्षम किया गया
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=एकल पृष्ठ में परिवर्त
pageExtracter.title=पृष्ठों को निकालें
pageExtracter.header=पृष्ठों को निकालें
pageExtracter.submit=निकालें
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -673,6 +704,7 @@ repair.submit=मरम्मत
#flatten
flatten.title=समतल करें
flatten.header=पीडीएफ़ समतल करें
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=समतल करें
@@ -720,6 +752,7 @@ extractImages.submit=निकालें
fileToPDF.title=फ़ाइल से पीडीएफ़
fileToPDF.header=किसी भी फ़ाइल को पीडीएफ़ में बदलें
fileToPDF.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice और Unoconv का उपयोग करती है।
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=समर्थित फ़ाइल प्रकार नीचे दिए गए होने चाहिए हालांकि समर्थित प्रारूपों की पूरी अद्यतन सूची के लिए कृपया LibreOffice दस्तावेज़ीकरण से संदर्भित करें
fileToPDF.submit=पीडीएफ़ में बदलें
@@ -756,11 +789,23 @@ merge.submit=मर्ज करें
pdfOrganiser.title=पेज व्यवस्थापक
pdfOrganiser.header=PDF पेज व्यवस्थापक
pdfOrganiser.submit=पृष्ठों को पुनः व्यवस्थित करें
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=पीडीएफ मल्टी टूल
multiTool.header=पीडीएफ मल्टी टूल
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=पीडीएफ देखें
@@ -771,6 +816,7 @@ pageRemover.title=पेज हटाने वाला
pageRemover.header=पीडीएफ पेज हटाने वाला
pageRemover.pagesToDelete=हटाने के पेज (पृष्ठ संख्याओं की व्यवस्था के लिए एक कॉमा से अलग संख्याओं की सूची दर्ज करें):
pageRemover.submit=पेज हटाएं
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=चुनें घुमाने का कोण (90 ड
rotate.submit=घुमाएं
#merge
#split-pdfs
split.title=पीडीएफ को विभाजित करें
split.header=पीडीएफ को विभाजित करें
split.desc.1=जिन नंबरों को आप चुनते हैं, वे पृष्ठ संख्या होती हैं जिन पर आप विभाजन करना चाहते हैं।
split.desc.2=इसलिए, 1,3,7-8 का चयन करना एक 10 पृष्ठों के दस्तावेज़ को 6 अलग-अलग पीडीएफ में विभाजित करेगा जैसे:
split.desc.2=इसलिए, 1,3,7-9 का चयन करना एक 10 पृष्ठों के दस्तावेज़ को 6 अलग-अलग पीडीएफ में विभाजित करेगा जैसे:
split.desc.3=दस्तावेज़ #1: पृष्ठ 1
split.desc.4=दस्तावेज़ #2: पृष्ठ 2 और 3
split.desc.5=दस्तावेज़ #3: पृष्ठ 4, 5 और 6
split.desc.5=दस्तावेज़ #3: पृष्ठ 4, 5, 6 और 7
split.desc.6=दस्तावेज़ #4: पृष्ठ 7
split.desc.7=दस्तावेज़ #5: पृष्ठ 8
split.desc.8=दस्तावेज़ #6: पृष्ठ 9 और 10
split.desc.8=दस्तावेज़ #6: पृष्ठ 10
split.splitPages=विभाजन करने के लिए पृष्ठ दर्ज करें:
split.submit=विभाजित करें
@@ -858,6 +904,8 @@ watermark.selectText.7=अपारदर्शिता (0% - 100%):
watermark.selectText.8=वॉटरमार्क प्रकार:
watermark.selectText.9=वॉटरमार्क छवि:
watermark.submit=वॉटरमार्क जोड़ें
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF से PDF/A में
pdfToPDFA.header=PDF से PDF/A में
pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए OCRmyPDF का उपयोग किया जाता है।
pdfToPDFA.submit=परिवर्तित करें
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=परिवर्तित करें
#PDFToHTML
PDFToHTML.title=PDF से HTML
PDFToHTML.header=PDF से HTML
PDFToHTML.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice का उपयोग करती है।
PDFToHTML.credit=यह सेवा फ़ाइल परिवर्तन के लिए pdftohtml का उपयोग करती है।
PDFToHTML.submit=परिवर्तित करें
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=लंबवत विभाजन की
split-by-sections.submit=PDF को विभाजित करें
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

View File

@@ -54,9 +54,25 @@ notAuthenticatedMessage=Felhasználó nincs hitelesítve.
userNotFoundMessage=A felhasználó nem található.
incorrectPasswordMessage=A jelenlegi jelszó helytelen.
usernameExistsMessage=Az új felhasználónév már létezik.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni
downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=Átalakítás
navbar.security=Biztonság
navbar.other=Egyéb
navbar.favorite=Favorites
navbar.darkmode=Sötét mód
navbar.pageOps=Lap műveletek
navbar.language=Languages
navbar.settings=Beállítások
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Beállítások
settings.update=Frisítés elérhető
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=App Verzió:
settings.downloadOption.title=Válassza ki a letöltési lehetőséget (Egyetlen fájl esetén a nem tömörített letöltésekhez):
settings.downloadOption.1=Nyissa meg ugyanabban az ablakban
@@ -107,8 +132,9 @@ settings.downloadOption.3=Töltse le a fájlt
settings.zipThreshold=Fájlok tömörítése, ha a letöltött fájlok száma meghaladja
settings.signOut=Kijelentkezés
settings.accountSettings=Fiókbeállítások
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Hitelesítés megváltoztatása
changeCreds.header=Frissítse fiókadatait
@@ -147,7 +173,7 @@ adminUserSettings.header=Adminisztrátori Felhasználói Vezérlési Beállítá
adminUserSettings.admin=Adminisztrátor
adminUserSettings.user=Felhasználó
adminUserSettings.addUser=Új felhasználó hozzáadása
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Szerepek
adminUserSettings.role=Szerep
adminUserSettings.actions=Műveletek
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználónév/jelszó megváltoztatására bejelentkezéskor
adminUserSettings.submit=Felhasználó mentése
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -422,6 +450,8 @@ login.rememberme=Emlékezz rám
login.invalid=Érvénytelen felhasználónév vagy jelszó!
login.locked=A fiókja zárolva lett!
login.signinTitle=Kérjük, jelentkezzen be!
login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel
login.oauth2AutoCreateDisabled=OAUTH2 Felhasználó automatikus létrehozása letiltva
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Átalakítás egyetlen oldallá
pageExtracter.title=Oldalak kinyerése
pageExtracter.header=Oldalak kinyerése
pageExtracter.submit=Kinyerés
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -673,6 +704,7 @@ repair.submit=Javítás
#flatten
flatten.title=Kiegyenlítés
flatten.header=PDF-ek kiegyenlítése
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Kiegyenlítés
@@ -720,6 +752,7 @@ extractImages.submit=Kinyerés
fileToPDF.title=Fájl PDF dokumentummá alakítása
fileToPDF.header=Konvertáljon bármilyen fájlt PDF dokumentummá
fileToPDF.credit=Ez a szolgáltatás a LibreOffice-t és az Unoconv-ot használja a fájlkonverzióhoz.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=A funkció az alábbi fájltípusokat támogatja, azonban a teljesen friss támogatott formátumok listájáért kérjük, tekintse meg a LibreOffice dokumentációját
fileToPDF.submit=Konvertálás PDF dokumentummá
@@ -756,11 +789,23 @@ merge.submit=Összevonás
pdfOrganiser.title=Oldalszervező
pdfOrganiser.header=PDF Oldalszervező
pdfOrganiser.submit=Oldalak átrendezése
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=PDF többfunkciós eszköz
multiTool.header=PDF többfunkciós eszköz
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=PDF megtekintése
@@ -771,6 +816,7 @@ pageRemover.title=Oldaltörlő
pageRemover.header=PDF oldaltörlő
pageRemover.pagesToDelete=Törlendő oldalak (adja meg az oldalszámok vesszővel elválasztott listáját):
pageRemover.submit=Oldalak törlése
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Válassza ki a forgatási szöget (90 fok egész számú töb
rotate.submit=Forgatás
#merge
#split-pdfs
split.title=PDF szétválasztás
split.header=PDF szétválasztás
split.desc.1=A kiválasztott számok a szétválasztani kívánt oldalszámok
split.desc.2=Például az 1,3,7-8 kiválasztása egy 10 oldalas dokumentumot 6 különálló PDF-fé szétválaszt
split.desc.2=Például az 1,3,7-9 kiválasztása egy 10 oldalas dokumentumot 6 különálló PDF-fé szétválaszt
split.desc.3=Dokumentum #1: Oldal 1
split.desc.4=Dokumentum #2: Oldal 2 és 3
split.desc.5=Dokumentum #3: Oldal 4, 5 és 6
split.desc.6=Dokumentum #4: Oldal 7
split.desc.7=Dokumentum #5: Oldal 8
split.desc.8=Dokumentum #6: Oldal 9 és 10
split.desc.5=Dokumentum #3: Oldal 4, 5, 6 és 7
split.desc.6=Dokumentum #4: Oldal 8
split.desc.7=Dokumentum #5: Oldal 9
split.desc.8=Dokumentum #6: Oldal 10
split.splitPages=Adja meg az oldalakat, amelyekre szét akarja választani:
split.submit=Szétválasztás
@@ -858,6 +904,8 @@ watermark.selectText.7=Átlátszóság (0% - 100%):
watermark.selectText.8=Vízjel típusa:
watermark.selectText.9=Vízjel képe:
watermark.submit=Vízjel hozzáadása
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF >> PDF/A
pdfToPDFA.header=PDF >> PDF/A
pdfToPDFA.credit=Ez a szolgáltatás az OCRmyPDF-t használja a PDF/A konverzióhoz
pdfToPDFA.submit=Konvertálás
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Konvertálás
#PDFToHTML
PDFToHTML.title=PDF >> HTML
PDFToHTML.header=PDF >> HTML
PDFToHTML.credit=Ez a szolgáltatás a LibreOffice-t használja a fájlkonverzióhoz.
PDFToHTML.credit=Ez a szolgáltatás a pdftohtml-t használja a fájlkonverzióhoz.
PDFToHTML.submit=Konvertálás
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Adja meg a függőleges szakaszok számá
split-by-sections.submit=Felosztás
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

View File

@@ -54,9 +54,25 @@ notAuthenticatedMessage=Pengguna tidak ter-autentikasi.
userNotFoundMessage=Pengguna tidak ditemukan.
incorrectPasswordMessage=Kata sandi saat ini salah.
usernameExistsMessage=Nama pengguna baru sudah ada.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini
downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Oops!
help=Help
goHomepage=Go to Homepage
joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository
donate=Donate
color=Color
sponsor=Sponsor
info=Info
###############
@@ -67,6 +83,8 @@ pipeline.uploadButton=Upload Custom
pipeline.configureButton=Configure
pipeline.defaultOption=Custom
pipeline.submitButton=Submit
pipeline.help=Pipeline Help
pipeline.scanHelp=Folder Scanning Help
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Validate
#############
# NAVBAR #
#############
navbar.convert=Konversi
navbar.security=Keamanan
navbar.other=Lain-lain
navbar.favorite=Favorites
navbar.darkmode=Mode Gelap
navbar.pageOps=Operasi Halaman
navbar.language=Languages
navbar.settings=Pengaturan
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
#############
settings.title=Pengaturan
settings.update=Pembaruan tersedia
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
settings.appVersion=Versi Aplikasi:
settings.downloadOption.title=Pilih opsi unduhan (Untuk unduhan berkas tunggal non zip):
settings.downloadOption.1=Buka di jendela yang sama
@@ -107,8 +132,9 @@ settings.downloadOption.3=Unduh berkas
settings.zipThreshold=Berkas zip ketika jumlah berkas yang diunduh melebihi
settings.signOut=Keluar
settings.accountSettings=Pengaturan Akun
settings.bored.help=Enables easter egg game
settings.cacheInputs.name=Save form inputs
settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Ubah Kredensial
changeCreds.header=Perbarui Detail Akun Anda
@@ -147,7 +173,7 @@ adminUserSettings.header=Pengaturan Kontrol Admin
adminUserSettings.admin=Admin
adminUserSettings.user=Pengguna
adminUserSettings.addUser=Tambahkan Pengguna Baru
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Peran
adminUserSettings.role=Peran
adminUserSettings.actions=Tindakan
@@ -158,6 +184,8 @@ adminUserSettings.demoUser=Demo User (No custom settings)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk
adminUserSettings.submit=Simpan Pengguna
adminUserSettings.changeUserRole=Ubah Peran Pengguna
adminUserSettings.authenticated=Authenticated
#############
# HOME-PAGE #
@@ -422,6 +450,8 @@ login.rememberme=Ingat saya
login.invalid=Nama pengguna atau kata sandi tidak valid.
login.locked=Akun Anda telah dikunci.
login.signinTitle=Silakan masuk
login.ssoSignIn=Masuk melalui Single Sign - on
login.oauth2AutoCreateDisabled=OAUTH2 Buat Otomatis Pengguna Dinonaktifkan
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Konversi ke Halaman Tunggal
pageExtracter.title=Ekstrak Halaman
pageExtracter.header=Ekstrak Halaman
pageExtracter.submit=Ekstrak
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
#getPdfInfo
@@ -673,6 +704,7 @@ repair.submit=Perbaiki
#flatten
flatten.title=Ratakan
flatten.header=Ratakan PDF
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Ratakan
@@ -720,6 +752,7 @@ extractImages.submit=Ekstrak
fileToPDF.title=Berkas ke PDF
fileToPDF.header=Mengonversi berkas apa pun ke PDF
fileToPDF.credit=Layanan ini menggunakan LibreOffice dan Unoconv untuk konversi berkas.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Jenis berkas yang didukung harus mencakup yang di bawah ini, namun untuk daftar lengkap format yang didukung, silakan lihat dokumentasi LibreOffice
fileToPDF.submit=Konversi ke PDF
@@ -756,11 +789,23 @@ merge.submit=Gabungkan
pdfOrganiser.title=Pengaturan Halaman
pdfOrganiser.header=Pengaturan Halaman PDF
pdfOrganiser.submit=Susun ulang halaman
pdfOrganiser.mode=Mode
pdfOrganiser.mode.1=Custom Page Order
pdfOrganiser.mode.2=Reverse Order
pdfOrganiser.mode.3=Duplex Sort
pdfOrganiser.mode.4=Booklet Sort
pdfOrganiser.mode.5=Side Stitch Booklet Sort
pdfOrganiser.mode.6=Odd-Even Split
pdfOrganiser.mode.7=Remove First
pdfOrganiser.mode.8=Remove Last
pdfOrganiser.mode.9=Remove First and Last
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool
multiTool.title=Alat Multi PDF
multiTool.header=Alat Multi PDF
multiTool.uploadPrompts=File Name
#view pdf
viewPdf.title=Lihat PDF
@@ -771,6 +816,7 @@ pageRemover.title=Penghapus Halaman
pageRemover.header=Penghapus Halaman PDF
pageRemover.pagesToDelete=Halaman yang akan dihapus (Masukkan daftar nomor halaman yang dipisahkan dengan koma) :
pageRemover.submit=Hapus Halaman
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Pilih sudut rotasi (dalam kelipatan 90 derajat):
rotate.submit=Rotasi
#merge
#split-pdfs
split.title=Membagi PDF
split.header=Membagi PDF
split.desc.1=Angka yang Anda pilih adalah nomor halaman yang ingin Anda pisahkan
split.desc.2=Dengan demikian, memilih 1,3,7-8 akan membagi dokumen 10 halaman menjadi 6 PDF terpisah:
split.desc.2=Dengan demikian, memilih 1,3,7-9 akan membagi dokumen 10 halaman menjadi 6 PDF terpisah:
split.desc.3=Dokumen #1: Halaman 1
split.desc.4=Dokumen #2: Halaman 2 dan 3
split.desc.5=Dokumen #3: Halaman 4, 5 dan 6
split.desc.6=Dokumen #4: Halaman 7
split.desc.7=Dokumen #5: Halaman 8
split.desc.8=Dokumen #6: Halaman 9 dan 10
split.desc.5=Dokumen #3: Halaman 4, 5, 6 dan 7
split.desc.6=Dokumen #4: Halaman 8
split.desc.7=Dokumen #5: Halaman 9
split.desc.8=Dokumen #6: Halaman 10
split.splitPages=Masukkan halaman yang akan dipisah:
split.submit=Pisahkan
@@ -858,6 +904,8 @@ watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Tipe Watermark:
watermark.selectText.9=Gambar Watermark:
watermark.submit=Tambahkan Watermark
watermark.type.1=Text
watermark.type.2=Image
#Change permissions
@@ -909,6 +957,8 @@ pdfToPDFA.title=PDF Ke PDF/A
pdfToPDFA.header=PDF ke PDF/A
pdfToPDFA.credit=Layanan ini menggunakan OCRmyPDF untuk konversi PDF/A.
pdfToPDFA.submit=Konversi
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Konversi
#PDFToHTML
PDFToHTML.title=PDF Ke HTML
PDFToHTML.header=PDF ke HTML
PDFToHTML.credit=Layanan ini menggunakan LibreOffice untuk konversi berkas.
PDFToHTML.credit=Layanan ini menggunakan pdftohtml untuk konversi berkas.
PDFToHTML.submit=Konversi
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Input angka untuk pembagian vertikal
split-by-sections.submit=Pisahkan PDF
split-by-sections.merge=Merge Into One PDF
#printFile
printFile.title=Print File
printFile.header=Print File to Printer
printFile.selectText.1=Select File to Print
printFile.selectText.2=Enter Printer Name
printFile.submit=Print
#licenses
licenses.nav=Licenses
licenses.title=3rd Party Licenses
@@ -1001,3 +1060,16 @@ licenses.version=Version
licenses.license=License
#error
error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
error.404.1=We can't seem to find the page you're looking for.
error.404.2=Something went wrong
error.github=Submit a ticket on GitHub
error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post

View File

@@ -17,11 +17,11 @@ true=Vero
false=Falso
unknown=Sconosciuto
save=Salva
saveToBrowser=Save to Browser
saveToBrowser=Salva nel browser
close=Chiudi
filesSelected=file selezionati
noFavourites=Nessun preferito
downloadComplete=Download Complete
downloadComplete=Download completo
bored=Stanco di aspettare?
alphabet=Alfabeto
downloadPdf=Scarica PDF
@@ -54,19 +54,37 @@ notAuthenticatedMessage=Utente non autenticato.
userNotFoundMessage=Utente non trovato.
incorrectPasswordMessage=La password attuale non è corretta.
usernameExistsMessage=Il nuovo nome utente esiste già.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
invalidUsernameMessage=Nome utente non valido, il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- o deve essere un indirizzo email valido.
deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
userAlreadyExistsOAuthMessage=L'utente esiste già come utente OAuth2.
userAlreadyExistsWebMessage=L'utente esiste già come utente web.
error=Errore
oops=Oops!
help=Aiuto
goHomepage=Vai alla Homepage
joinDiscord=Unisciti al nostro server Discord
seeDockerHub=Vedi DockerHub
visitGithub=Visita il repository Github
donate=Donazione
color=Colore
sponsor=Sponsor
info=Info
###############
# Pipeline #
###############
pipeline.header=Pipeline Menu (Beta)
pipeline.header=Menù pipeline (Beta)
pipeline.uploadButton=Caricamento personalizzato
pipeline.configureButton=Configura
pipeline.defaultOption=Personalizzato
pipeline.submitButton=Invia
pipeline.help=Aiuto sulla pipeline
pipeline.scanHelp=Aiuto per la scansione delle cartelle
######################
# Pipeline Options #
@@ -87,18 +105,25 @@ pipelineOptions.validateButton=Convalidare
#############
# NAVBAR #
#############
navbar.convert=Converti
navbar.security=Sicurezza
navbar.other=Altro
navbar.favorite=Preferiti
navbar.darkmode=Modalità Scura
navbar.pageOps=Modifica pagine
navbar.language=Lingue
navbar.settings=Impostazioni
navbar.allTools=Strumenti
navbar.multiTool=Strumenti multipli
navbar.sections.organize=Organizza
navbar.sections.convertTo=Converti in PDF
navbar.sections.convertFrom=Converti da PDF
navbar.sections.security=Firma Firma & Sicurezza
navbar.sections.advance=Avanzate
navbar.sections.edit=Visualizza & Modifica
#############
# SETTINGS #
#############
settings.title=Impostazioni
settings.update=Aggiornamento disponibile
settings.updateAvailable={0} è la versione attualmente installata. Una nuova versione ({1}) è disponibile.
settings.appVersion=Versione App:
settings.downloadOption.title=Scegli opzione di download (Per file singoli non compressi):
settings.downloadOption.1=Apri in questa finestra
@@ -107,8 +132,9 @@ settings.downloadOption.3=Scarica file
settings.zipThreshold=Comprimi file in .zip quando il numero di download supera
settings.signOut=Logout
settings.accountSettings=Impostazioni Account
settings.bored.help=Abilita easter egg game
settings.cacheInputs.name=Salva gli input del modulo
settings.cacheInputs.help=Abilitare per memorizzare gli input utilizzati in precedenza per esecuzioni future
changeCreds.title=Cambia credenziali
changeCreds.header=Aggiorna i dettagli del tuo account
@@ -116,7 +142,7 @@ changeCreds.changePassword=Stai utilizzando le credenziali di accesso predefinit
changeCreds.newUsername=Nuovo nome utente
changeCreds.oldPassword=Password attuale
changeCreds.newPassword=Nuova Password
changeCreds.confirmNewPassword=Conferma Nuova Password
changeCreds.confirmNewPassword=Conferma nuova Password
changeCreds.submit=Invia modifiche
@@ -147,17 +173,19 @@ adminUserSettings.header=Impostazioni di controllo utente amministratore
adminUserSettings.admin=Amministratore
adminUserSettings.user=Utente
adminUserSettings.addUser=Aggiungi un nuovo Utente
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
adminUserSettings.usernameInfo=Il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- oppure deve essere un indirizzo email valido.
adminUserSettings.roles=Ruoli
adminUserSettings.role=Ruolo
adminUserSettings.actions=Azioni
adminUserSettings.apiUser=Utente API limitato
adminUserSettings.extraApiUser=Additional Limited API User
adminUserSettings.extraApiUser=API utente limitato aggiuntivo
adminUserSettings.webOnlyUser=Utente solo Web
adminUserSettings.demoUser=Utente demo (nessuna impostazione personalizzata)
adminUserSettings.internalApiUser=Internal API User
adminUserSettings.internalApiUser=API utente interna
adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso
adminUserSettings.submit=Salva utente
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
adminUserSettings.authenticated=Autenticato
#############
# HOME-PAGE #
@@ -176,7 +204,7 @@ multiTool.tags=Strumento multiplo,operazione multipla,interfaccia utente,trascin
home.merge.title=Unisci
home.merge.desc=Unisci facilmente più PDF in uno.
merge.tags=unione, operazioni sulla pagina, back end, lato server
merge.tags=unione,operazioni sulla pagina,back-end,lato server
home.split.title=Dividi
home.split.desc=Dividi un singolo PDF in più documenti.
@@ -245,7 +273,7 @@ ocr.tags=riconoscimento,testo,immagine,scansione,lettura,identificazione,rilevam
home.extractImages.title=Estrai immagini
home.extractImages.desc=Estrai tutte le immagini da un PDF e salvale come zip.
extractImages.tags=immagine,photo,save,archive,zip,capture,grab
extractImages.tags=immagine,foto,salva,archivio,zip,catturare,prendere
home.pdfToPDFA.title=Converti in PDF/A
home.pdfToPDFA.desc=Converti un PDF nel formato PDF/A per archiviazione a lungo termine.
@@ -393,11 +421,11 @@ overlay-pdfs.tags=Svrapponi
home.split-by-sections.title=Dividi PDF per sezioni
home.split-by-sections.desc=Dividi ciascuna pagina di un PDF in sezioni orizzontali e verticali più piccole
split-by-sections.tags=Dividi sezione, dividi, personalizza
split-by-sections.tags=Dividi sezione,dividi,personalizza
home.AddStampRequest.title=Aggiungi timbro al PDF
home.AddStampRequest.desc=Aggiungi testo o aggiungi timbri immagine nelle posizioni prestabilite
AddStampRequest.tags=Timbro, Aggiungi immagine, Centra immagine, Filigrana, PDF, Incorpora, Personalizza
AddStampRequest.tags=Timbro,Aggiungi immagine,Centra immagine,Filigrana,PDF,Incorpora,Personalizza
home.PDFToBook.title=PDF in libro
@@ -422,6 +450,8 @@ login.rememberme=Ricordami
login.invalid=Nome utente o password errati.
login.locked=Il tuo account è stato bloccato.
login.signinTitle=Per favore accedi
login.ssoSignIn=Accedi tramite Single Sign-on
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
#auto-redact
@@ -454,6 +484,7 @@ pdfToSinglePage.submit=Converti in pagina singola
pageExtracter.title=Estrai pagine
pageExtracter.header=Estrai pagine
pageExtracter.submit=Estrai
pageExtracter.placeholder=(es. 1,2,8 o 4,7,12-16 o 2n-1)
#getPdfInfo
@@ -630,7 +661,7 @@ removeBlanks.submit=Rimuovi
#removeAnnotations
removeAnnotations.title=Rimuovi Annotazioni
removeAnnotations.header=Remuovi Annotazioni
removeAnnotations.header=Rimuovi Annotazioni
removeAnnotations.submit=Rimuovi
@@ -671,8 +702,9 @@ repair.submit=Ripara
#flatten
flatten.title=Appiattisci
flatten.title=Appiattire
flatten.header=Appiattisci PDF
flatten.flattenOnlyForms=Appiattisci solo i moduli
flatten.submit=Appiattisci
@@ -720,6 +752,7 @@ extractImages.submit=Estrai
fileToPDF.title=Converti file in PDF
fileToPDF.header=Converti qualsiasi file in PDF
fileToPDF.credit=Questo servizio utilizza LibreOffice e Unoconv per la conversione dei file.
fileToPDF.supportedFileTypesInfo=Tipi di file supportati
fileToPDF.supportedFileTypes=I formati file supportati dovrebbero includere quelli sottostanti. Tuttavia, per una lista aggiornata controlla la documentazione di LibreOffice
fileToPDF.submit=Converti in PDF
@@ -756,11 +789,23 @@ merge.submit=Unisci
pdfOrganiser.title=Organizza pagine
pdfOrganiser.header=Organizza le pagine di un PDF
pdfOrganiser.submit=Riordina pagine
pdfOrganiser.mode=Modalità
pdfOrganiser.mode.1=Ordine delle pagine personalizzato
pdfOrganiser.mode.2=Ordine inverso
pdfOrganiser.mode.3=Ordinamento fronte-retro
pdfOrganiser.mode.4=Ordinamento a libretto
pdfOrganiser.mode.5=Ordinamento libretto con cucitura laterale
pdfOrganiser.mode.6=Divisione pari-dispari
pdfOrganiser.mode.7=Rimuovi prima
pdfOrganiser.mode.8=Rimuovi ultima
pdfOrganiser.mode.9=Rimuovi la prima e l'ultima
pdfOrganiser.placeholder=(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1)
#multiTool
multiTool.title=Multifunzione PDF
multiTool.header=Multifunzione PDF
multiTool.uploadPrompts=Nome file
#view pdf
viewPdf.title=Visualizza PDF
@@ -771,6 +816,7 @@ pageRemover.title=Rimuovi pagine
pageRemover.header=Rimuovi pagine da un PDF
pageRemover.pagesToDelete=Pagine da eliminare (inserisci una lista di numeri separati da virgola):
pageRemover.submit=Rimuovi pagine
pageRemover.placeholder=(es. 1,2,6 o 1-10,15-30)
#rotate
@@ -780,17 +826,17 @@ rotate.selectAngle=Scegli angolo di rotazione (in multipli di 90 gradi):
rotate.submit=Ruota
#merge
#split-pdfs
split.title=Dividi PDF
split.header=Dividi PDF
split.desc.1=I numeri che scegli sono le pagine a cui desideri dividere il documento
split.desc.2=Per esempio inserendo 1,3,7-8 separeresti un documento di 10 pagine in 6 diversi PDF con:
split.desc.2=Per esempio inserendo 1,3,7-9 separeresti un documento di 10 pagine in 6 diversi PDF con:
split.desc.3=Documento #1: Pagina 1
split.desc.4=Documento #2: Pagine 2 e 3
split.desc.5=Documento #3: Pagine 4, 5 e 6
split.desc.6=Documento #4: Pagina 7
split.desc.7=Documento #5: Pagina 8
split.desc.8=Documento #6: Pagine 9 e 10
split.desc.5=Documento #3: Pagine 4, 5, 6 e 7
split.desc.6=Documento #4: Pagina 8
split.desc.7=Documento #5: Pagina 9
split.desc.8=Documento #6: Pagine 10
split.splitPages=Inserisci pagine a cui dividere:
split.submit=Dividi
@@ -858,6 +904,8 @@ watermark.selectText.7=Opacità (0% - 100%):
watermark.selectText.8=Tipo di filigrana:
watermark.selectText.9=Immagine filigrana:
watermark.submit=Aggiungi Filigrana
watermark.type.1=Testo
watermark.type.2=Immagine
#Change permissions
@@ -901,7 +949,7 @@ changeMetadata.subject=Oggetto:
changeMetadata.trapped=Recuperato:
changeMetadata.selectText.4=Altre proprietà:
changeMetadata.selectText.5=Aggiungi proprietà personalizzata:
changeMetadata.submit=Cambia Proprietà
changeMetadata.submit=Cambia proprietà
#pdfToPDFA
@@ -909,6 +957,8 @@ pdfToPDFA.title=Da PDF a PDF/A
pdfToPDFA.header=Da PDF a PDF/A
pdfToPDFA.credit=Questo servizio utilizza OCRmyPDF per la conversione in PDF/A.
pdfToPDFA.submit=Converti
pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente
pdfToPDFA.outputFormat=Formato di output
#PDFToWord
@@ -938,7 +988,7 @@ PDFToText.submit=Converti
#PDFToHTML
PDFToHTML.title=Da PDF a HTML
PDFToHTML.header=Da PDF a HTML
PDFToHTML.credit=Questo servizio utilizza LibreOffice per la conversione.
PDFToHTML.credit=Questo servizio utilizza pdftohtml per la conversione.
PDFToHTML.submit=Converti
@@ -992,6 +1042,15 @@ split-by-sections.vertical.placeholder=Inserire il numero di divisioni verticali
split-by-sections.submit=Dividi PDF
split-by-sections.merge=Unisci in un unico PDF
#printFile
printFile.title=Stampa file
printFile.header=Stampa file su stampante
printFile.selectText.1=Seleziona file da stampare
printFile.selectText.2=Inserire il nome della stampante
printFile.submit=Stampare
#licenses
licenses.nav=Licenze
licenses.title=Licenze di terze parti
@@ -1001,3 +1060,16 @@ licenses.version=Versione
licenses.license=Licenza
#error
error.sorry=Ci scusiamo per il problema!
error.needHelp=Hai bisogno di aiuto / trovato un problema?
error.contactTip=Se i problemi persistono, non esitare a contattarci per chiedere aiuto. Puoi inviare un ticket sulla nostra pagina GitHub o contattarci tramite Discord:
error.404.head=404 - Pagina non trovata | Spiacenti, siamo inciampati nel codice!
error.404.1=Non riusciamo a trovare la pagina che stai cercando.
error.404.2=Qualcosa è andato storto
error.github=Invia un ticket su GitHub
error.showStack=Mostra traccia dello stack
error.copyStack=Copia traccia dello stack
error.githubSubmit=GitHub: invia un ticket
error.discordSubmit=Discord: invia post di supporto

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