Compare commits

...

82 Commits

Author SHA1 Message Date
Anthony Stirling
32e46eeb73 Update build.gradle 2024-12-03 10:54:07 +00:00
Omar Ahmed Hassan
b7da84d257 Fix deserialization failure in Change Metadata (#2382)
* Fix deserialization failure from String to Map

Fix deserialization failure from String to Map that caused the following exception:
Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity<byte[]> stirling.software.SPDF.controller.api.misc.MetadataController.metadata(stirling.software.SPDF.model.api.misc.MetadataRequest) throws java.io.IOException: [Field error in object 'metadataRequest' on field 'allRequestParams': rejected value [{"customKey1" : "YourCustomKey", "customKeyValue1", "YourCustomValue"}]; codes [typeMismatch.metadataRequest.allRequestParams,typeMismatch.allRequestParams,typeMismatch.java.util.Map,typeMismatch];

* Fix form binding for dynamic Map entries in Change Metadata

- Implemented support for dynamic key-value inputs in Change Metadata form using proper `name` attributes for Map (`allRequestParams`) binding.
- Fix form binding for dynamic Map (`allRequestParams`) entries in Change Metadata as the `allRequestParams` (Map name) was being sent as an empty map.
2024-12-03 08:28:34 +00:00
github-actions[bot]
1c1ead5d62 📝 Update README: Translation Progress Table (#2381)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-02 19:25:16 +00:00
albanobattistella
6ff53aa5b3 Update messages_it_IT.properties (#2380) 2024-12-02 18:59:05 +00:00
github-actions[bot]
8d60b08cd9 📝 Update README: Translation Progress Table (#2379)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-02 18:32:07 +00:00
github-actions[bot]
64cf5167c0 Update translation files (#2378)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-02 18:18:16 +00:00
Omar Ahmed Hassan
de4637e8d4 Fix drag and drop area for file choosers by adding separate ones (#2368)
* Add separate drag and drop area for file choosers

 - Add separate drag and drop area for file choosers

### Why?
Previously, when there were multiple file choosers in the same page, if you attempted to drag and drop any files, they would be added to both file choosers as it was designed at first to handle 1 file chooser present, now that we have multiple ones, it is necessary to adapt our design to match the changing functionality.

### Can you not preserve the old overlay when there's only one file chooser present?
Yes, we can, but imagine as a user, you try to drag and drop a file in one page and the fields turn into drag and drop areas then you go to another page and try to drag and drop again but you encounter the old overlay instead, as a user you might get confused and ask yourself "What changed?" or if a user is telling another user the steps to drag and drop files and he didn't know about this case, then it would still be confusing, thus consistency is preferred in this case.

* Update file chooser UI

* Add support for listing and removing selected files and their file icons

- Selected files are listed below the file chooser in a selected files container.
- Users can now remove uploaded/selected files.
- Hide selected files container/box unless there are files selected/uploaded.
- Add separate overlay for each drag & drop area.

## FAQ:
- Why did you assign a unique id to each file? isn't the filename enough?
= Because a user might upload multiple files with the same name, if the user wanted to remove one of them, how would we differentiate between them? we won't be able to unless we assign an identifier, you might argue "we remove based on the filename and size", then what if the user uploaded the same file more than once (intentionally), then we would accidentally remove all the files even though that is not what the user wanted, so going with unique ID approach would prevent this issue/problem from occurring in the first place.

* Rename remove-file css class to remove-selected-file

- Rename remove-file css class to remove-selected-file to avoid css conflict with remove-file in merge.css

* Use input element to dispatch event on file removal

Use the correct element to dispatch "file-input-change" (input element is the correct one).

* Adapt file chooser UI to themes

- Adapt file chooser UI to themes by adjusting their font colors and background colors.
- Make text more visible in overlay by increasing the font size by 0.1rem and setting font weight to 550.

* Remove extra overlay border

- Removing overlay's border as it is unnecessary and only causing a double border issue on the file input container.

* Remove Browse button, highlight file chooser and make it clickable

- Remove browse button.
- Make the entire file chooser container clickable.
- Add glowing effect on hover for file chooser.
- Change color of file chooser on hover.

* Replace crypto.randomUUID() with UUID.uuidv4()

- Replace crypto.randomUUID() with UUID.uuidv4() as crypto.randomUUID() is only supported in secured contexts such as localhost 127.0.0.1 and over HTTPS

* Fix merge file removal not being reflected in file chooser

- Files removed from the list in merge page would now be reflected in the file chooser's container.

* Make inputElement optional in removeFileById

- Make inputElement optional in removeFileById, this way we could control changing inputElements files.

* Add translation support to file chooser

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-12-02 18:10:12 +00:00
Sai Kumar
3c0a8071dc added support for new line break in stampController (#2370)
added support for new line in stampController

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-12-02 17:48:19 +00:00
Omar Ahmed Hassan
04ccdf6f76 Fix: prevent fileInput.js from adding event listeners more than once (#2365)
Fix fileInput.js adding event listeners more than once

- Fix a bug that caused fileInput.js to add event listeners more than once per HTML file as it's included in fileSelector fragment in fragments/common.html thus it's being loaded N times where N is the number of file selectors / custom file chooser / file input elements per HTML file, which resulted in each event actions being executed N times as well, which was prevalent in drag and drop operations such as dragging and dropping a file called y.png, it would be duplicated N times (as in /sign path).
2024-12-02 17:41:11 +00:00
Omar Ahmed Hassan
db02fba31f Fix translations for watermark spacers (#2369)
Fix translations by adding a space between width/height and spacer and capitalize the first letter
2024-12-02 17:01:19 +00:00
Omar Ahmed Hassan
5b6f649e4e Fix submit button in crop by adding id (#2374)
- Add missing ID to submit button in crop page.
2024-12-02 10:40:46 +00:00
Omar Ahmed Hassan
de23bb702c Fix allowing multiple files to be dropped onto a single file input (#2359)
Fix a bug that allowed multiple files to be dropped onto a single-file input element

- Fix a bug that allowed multiple files to be dropped onto a single-file input element by accepting only the first file.
2024-11-29 17:31:14 +00:00
github-actions[bot]
25e564154e Update 3rd Party Licenses (#2362)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-11-29 15:41:29 +00:00
Anthony Stirling
3633a979d3 fixes and other changes and debug of WIP SAML (#2360)
* backup

* remove debugs

* oauth to saml and compare fixes etc

* ee flag for saml

* more fixes

* info to debug

* remove unused repo

* spring dev fix for saml

* debugs

* saml stuff

* debugs

* fix
2024-11-29 15:11:59 +00:00
Omar Ahmed Hassan
99d481d69f Fix Array.from syntax in nonmultiple file upload (#2357)
- Fix Array.from syntax in nonmultiple file upload as Array.from(<non-array or string>) returns an empty array which is the case when a file is selected from an input element (when multiple attribute isn't  supported) which can be found in Array.from(element.files[0]) -> results in an empty array.
2024-11-29 12:22:52 +00:00
github-actions[bot]
a5ba6c403a 📝 Update README: Translation Progress Table (#2356)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-28 15:39:39 +00:00
albanobattistella
b2e6d89d16 Update messages_it_IT.properties (#2355) 2024-11-28 14:42:55 +00:00
github-actions[bot]
b59d2d15b4 Update translation files (#2354)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-28 14:42:29 +00:00
Omar Ahmed Hassan
61e750646c Feature: Undo Redo options multi tool #2297 (#2348)
* Implement Command class for Command Pattern

Created a base `Command` class to implement the **Command Pattern**. This class provides a skeletal implementation for `execute`, `undo`, and `redo` methods.

**Note:** This class is intended to be subclassed and not instantiated directly.

* Add undo/redo stacks and operations

* Use rotate element command to perform execute/undo/redo operations

* Handle commands executed through events
- Add "command-execution" event listener to execute commands that are not invoked from the same class while adding the command to the undo stack and clearing the redo stack.

* Add and use rotate all command to rotate/redo/undo all elements

* Use command pattern to delete pages

* Use command pattern for page selection

* Use command pattern to move pages up and down

* Use command pattern to remove selected pages

* Use command pattern to perform the splitting operation

* Add undo/redo functionality with filename input exclusion

- Implement undo (Ctrl+Z) and redo (Ctrl+Y) functionality.
- Prevent undo/redo actions when the filename input field is focused.
- Ensures proper handling of undo/redo actions without interfering with text editing.

* Introduce UndoManager for managing undo/redo operations

 - Encapsulate undo/redo stacks and operations within UndoManager.
- Simplify handling of undo/redo functionality through a dedicated manager.

* Call execute on splitAllCommand

- Fix a bug that caused split all functionality to not work as execute() wasn't called on splitAllCommand

* Add undo/redo buttons to multi tool

- Add undo/redo buttons to multi tool
- Dispatch an event upon state change (such as changes in the undo/redo stacks) to update the UI accordingly.

* Add undo/redo to translations

* Replace hard-coded "Undo"/"Redo" with translation keys in multi tool

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-28 14:25:13 +00:00
Omar Ahmed Hassan
de9c21b3de Fix: page break insertion functionality in Multi Tool (#2350)
Fix page break insertion functionality

- Page Break insertion functionality now successfully inserts page breaks upon request
2024-11-28 10:21:14 +00:00
Nureddin Farzaliyev
b32d6cb858 Azerbaijani Language Translation (#2347)
* Azerbaijani flag and dropdown item added

* Azerbaijani Language file Added

* AZ - ignore_translation.toml init

* AZ Translation Enterprise Edition Section

* Translation for Generic

* translation-az pipeline

* Translation for Analytics

* Translation for NAVBAR

* Translation for SETTINGS

* translation-az homepage

* Translation for #login

* Translation for (showJS)

* Translation for #showJS

* Translation for #PDFToWord

* Translation for #PDFToPresentation

* Translation for #PDFToText

* Translation for #PDFToHTML

* Translation for #PDFToXML

* Translation for #PDFToCSV

* Translation for #repair

* Translation for #pageLayout

* Translation for #pdfToSinglePage

* Translation for #pageExtracter

* Translation for #getPdfInfo

* Translation for #markdown-to-pdf

* Translation for #PDFToXML

* Translation for #html-to-pdf

* Translation for #PDFToHTML

* Translation for #PDFToText

* Translation for #PDFToPresentation

* Translation for #PDFToWord

* Translation for #PDFToCSV

* Translation for #url-to-pdf

* Translation for #pdfToImage

* Translation for #BookToPDF

* Translation for #PDFToBook

* Translation for #autoRedact

* Translation for #Add image

* Translation for #File to PDF

* Translation for (remove-image)

* Translation for (remove-image)

* Translation for (survey)

* Translation for (licenses)

* Translation for (printFile)

* Translation for (split-bysections)

* Translation for (overlay-pdfs)

* Translation for (split-by-size-or-count)

* Translation for (addPageNumbers)

* Translation for (adjustContrast)

* Translation for (autoSplitPDF)

* Translation for (scalePages)

* Translation for (removeCertSign)

* Translation for (removeAnnotations)

* Translation for (sign)

* Translation for (flatten)

* Translation for (extractImages)

* Translation for (merge)

* Translation for (view pdf)

* Translation for (pageRemover)

* Translation for (rotate)

* az Translation for replace-invert-color

* az Translation for addstamprequest

* Translation for (remove-image)

* Translation for #url-to-pdf

* Update messages_az_AZ.properties

* Update README.md

* Update README.md

* Update messages_az_AZ.properties

* Translation for #compress

* Translation for #Change permissions

* Translation for #remove password

* Translation for #pdfToPDFA

* Translation for #changeMetadata

* Translation for #merge

* Translation for #split-pdfs

* translation-az addpass

* translation-az watermark

* translation-az removeblanks

* translation-az compare

* translation-az certsign

* Translation for #pdfOrganiser

* Translation for #multiTool

* Translation for #multiTool

* az translation scannerimagesplit

* az translation ocr

* az translation fix

* az translation linebreak fix

* az translation linebreak fix

---------

Co-authored-by: Lucifer25x <lucifer25x@protonmail.com>
Co-authored-by: islamd7 <vusal04999@gmail.com>
Co-authored-by: Valida Rahmanova <validerehmanova04@gmail.com>
Co-authored-by: yusif043-bit <yusif.abbaszade.043@gmail.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-27 18:09:42 +00:00
pixeebot[bot]
d832a90de0 (CodeQL) Fixed finding: "Arbitrary file access during archive extraction ("Zip Slip")
" (#2344)

(CodeQL) Fixed finding: "Arbitrary file access during archive extraction ("Zip Slip")
"

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2024-11-27 07:16:03 +00:00
Anthony Stirling
212e521238 Update MetricsAggregatorService.java 2024-11-26 21:30:47 +00:00
github-actions[bot]
0915e72a3d 📝 Update README: Translation Progress Table (#2341)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-26 20:52:54 +00:00
github-actions[bot]
ee5013651f Update 3rd Party Licenses (#2342)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-11-26 20:52:39 +00:00
github-actions[bot]
4aa44e6fc0 Update translation files (#2343)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-26 20:51:57 +00:00
github-actions[bot]
41c743a9f8 Update 3rd Party Licenses (#2337)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-11-26 20:51:02 +00:00
Anthony Stirling
833b3c45c6 Removal of Ghostscript to use qpdf and tesseract directly (#2338)
* navbar fix multi tool and compress location

* release notes and ghostscript removal

* cleanups

* formatting

* update docs

* more

* more

* docs

* release bump

* Hardening suggestions for Stirling-PDF / ghostscript (#2339)

* Protect `readLine()` against DoS

* Sanitized user-provided file names in HTTP multipart uploads

---------

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

---------

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2024-11-26 20:50:35 +00:00
Omar Ahmed Hassan
654bc94d44 Fix: input file overwrite in merge (#2335)
* Fix input files being overwritten by newly uploaded files

- Fix a bug that caused existing selected/uploaded files to be overwritten when a new input file is uploaded through input element.
- Add source property to change event to differentiate between uploaded files using input element and drag/drop uploads to avoid processing drag/drop files more than once, thus avoiding file duplication (file duplication resulting from copying drop/drop files to input files on each 'change' event).

* Dispatch and use file-input-change instead of change event for merging

- Dispatch "file-input-change" event after each "change" event in file upload, to notify other functions/components relying on the files provided by the \<input\> element.
- Use "file-input-change" instead of "change" event to display the latest version of uploaded files.

# FAQ:
- Why use "file-input-change" instead of "change" in merge.js?
= "change" event is automatically triggered when a file is uploaded through \<input\> element which would replace all the existing selected/uploaded files including the drag/drop files.

## Example:
Let's say that the user wants to upload/select the x.pdf, y.pdf and z.pdf all together:

- user selects "x.pdf" -> file selected successfully.
= selected files: x.pdf

- user drags and drops "y.pdf" -> file dropped successfully
= selected files: x.pdf, y.pdf

- user selects again using \<input\> "z.pdf" -> file selected succesfully overwriting selected files.
= selected files: z.pdf
2024-11-26 20:41:08 +00:00
dependabot[bot]
86fa404c90 Bump commons-io:commons-io from 2.17.0 to 2.18.0 (#2333)
Bumps commons-io:commons-io from 2.17.0 to 2.18.0.

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  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-11-26 20:38:51 +00:00
Anthony Stirling
1db1370420 Update README.md 2024-11-26 10:26:44 +00:00
github-actions[bot]
ee4b7e02ab 📝 Update README: Translation Progress Table (#2327)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-26 08:17:56 +00:00
albanobattistella
e6c5634165 Update messages_it_IT.properties (#2334) 2024-11-26 08:17:09 +00:00
Anthony Stirling
5188eb3b04 Update build.gradle 2024-11-26 08:16:45 +00:00
Anthony Stirling
3fa6bcb2ee navbar fix multi tool and compress location (#2331) 2024-11-25 21:42:49 +00:00
github-actions[bot]
0b359ad4a8 Update translation files (#2329)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-25 20:55:56 +00:00
reecebrowne
23ee77f6ab Additional sign tooltips (#2328)
* Add tooltip to sign add to all pages feature

* Additional Tooltips
2024-11-25 20:43:05 +00:00
github-actions[bot]
bfc1ed2b39 Update translation files (#2326)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-25 20:13:03 +00:00
reecebrowne
da46d942ba Add tooltip to sign add to all pages feature (#2325) 2024-11-25 20:11:27 +00:00
Anthony Stirling
5936e856f0 metrics 2024-11-25 14:02:17 +00:00
github-actions[bot]
fd906d36dd Update 3rd Party Licenses (#2321)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-11-24 17:21:54 +00:00
Ludy
8f4709d82e Bump com.h2database:h2 from 2.1.214 to 2.3.232 (#2314) 2024-11-24 14:36:53 +00:00
dependabot[bot]
8445f2719b Bump org.springframework:spring-webmvc from 6.1.14 to 6.2.0 (#2268)
Bumps [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) from 6.1.14 to 6.2.0.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.0)

---
updated-dependencies:
- dependency-name: org.springframework:spring-webmvc
  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>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-24 14:36:30 +00:00
github-actions[bot]
eaa64e1471 Update 3rd Party Licenses (#2318)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-11-24 12:16:01 +00:00
Ludy
4abb0cb85e Fix: id for submit button added (#2320) 2024-11-24 10:31:50 +00:00
Omar Ahmed Hassan
afad06bed4 Extract tables from PDF to CSV using Tabula (#2312)
* Add Tabula dependency and exclude slf4j-simple

- Add tabula-java dependency to extract tables into CSV.
- Exclude slf4j-simple due to Logback

* Add a flexible CSVWriter

- Add FlexibleCSVWriter which extends CSVWriter to pass a custom CSVFormat, as CSVWriter's parameterized constructor (that allows changing CSVFormat) is protected.

* Use Tabula in extracting tables from PDF

- Use Tabula in extracting tables from PDF instead of the existing implementation

* Delete PDFTableStripper as It is unneeded

- Delete PDFTableStripper as It is unneeded as Tabula-Java is used instead.

* Use correct class in ExtractCSVController logger

* Exclude gson and bcprov-jdk15on dependencies from tabula

- Exclude gson and bcprov-jdk15on from tabula-java due to detected security vulnerabilities.
2024-11-23 23:28:44 +00:00
github-actions[bot]
faa8a9752c 📝 Update README: Translation Progress Table (#2317)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-23 23:05:40 +00:00
Omar Ahmed Hassan
989538e340 Update Arabic Language for Multi tool section (#2316)
* Update Arabic Language for Multi tool section

* Add a back line at 955-956
2024-11-23 22:36:21 +00:00
Ludy
5b8bdc3352 improves readability (#2313) 2024-11-23 22:09:46 +00:00
Thomas BERNARD
f559eaa4e8 French translation (again) (#2315)
* French translations for multiTool

* fix french translation invalidPasswordMessage/confirmPasswordErrorMessage

* french translation : reset/navbar.search/sign.saved

* fix my invalidPasswordMessage french translation :)
2024-11-23 22:09:27 +00:00
github-actions[bot]
f306e00fba Update 3rd Party Licenses (#2310)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-11-23 12:16:20 +00:00
github-actions[bot]
e09d6f9998 📝 Update README: Translation Progress Table (#2311)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-23 12:16:16 +00:00
Ludy
3a27aa16d5 Improves security when processing properties files (#2303)
* Improves security when processing properties files

* Check for spaces in the key

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-23 11:49:49 +00:00
Ludy
9a96109ea2 Fix: Prevents duplicate listing of search results (#2306) 2024-11-23 11:37:13 +00:00
albanobattistella
ad1cce378f Update messages_it_IT.properties (#2307)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-23 10:53:26 +00:00
Ludy
9abb105835 Fix: Fixes dependency bug and replaces obsolete method (#2309) 2024-11-23 10:51:17 +00:00
Renan
204bae3bc1 Sign multiple PDF pages at the same time in the same location (#2008) (#2278)
* Sign multiple PDF pages at the same time in the same location (#2008)

* Modifying the functionality of how the signature is added to all pages (#2008)

* Adding the functionality to reverse the addition on all pages and implementing buttons to navigate to the first and last pages (#2008)
2024-11-22 17:40:09 +00:00
reecebrowne
547f23fe78 Posthog multitool (#2301)
* Posthog functionality

* Posthog in multitool

* check if anylitics enabled
2024-11-22 17:38:44 +00:00
Rafael Encinas
543ad083a2 Fix file clear for errors (#2302)
* Prevent file input from being removed when an error occurs

* Fix a bug preventing fetch when 'Bored waiting' btn isn't present
2024-11-22 17:11:23 +00:00
github-actions[bot]
61bccd1d8b 📝 Update README: Translation Progress Table (#2300)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-22 15:27:11 +00:00
github-actions[bot]
83be709299 Update translation files (#2298)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-22 15:17:04 +00:00
reecebrowne
0e602153f3 Feature/2198/multitool multi select move pages (#2294)
* Multitool - Select multiple pages for rotation tool

* Multitool multi select delete feature

* Multitool multi select UI improvements and big fixes

* Multitool multi select select all and UI improvements

* Multi tool multi select, download selected, clean up and bug fixes

* Groundwork for multiselect drag and drop

* Multi select drag and drop finalised

* Update translation files

Signed-off-by: GitHub Action <action@github.com>

* Turn off select mode after multidrag

---------

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-22 11:39:22 +00:00
Anthony Stirling
597619740a Update build.gradle 2024-11-22 09:29:41 +00:00
github-actions[bot]
41a39a0a94 Update 3rd Party Licenses (#2295)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-11-22 09:22:03 +00:00
dependabot[bot]
b14fba064d Bump org.springframework.security:spring-security-saml2-service-provider from 6.3.4 to 6.4.1 (#2296)
Bump org.springframework.security:spring-security-saml2-service-provider

Bumps [org.springframework.security:spring-security-saml2-service-provider](https://github.com/spring-projects/spring-security) from 6.3.4 to 6.4.1.
- [Release notes](https://github.com/spring-projects/spring-security/releases)
- [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-projects/spring-security/compare/6.3.4...6.4.1)

---
updated-dependencies:
- dependency-name: org.springframework.security:spring-security-saml2-service-provider
  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-11-22 09:19:58 +00:00
alonsofabila-dev
bd29dd1ac3 Bored waiting button doesnt remove itself after processing (#2079) (#2235)
Fix: Bored waiting button doesnt remove itself after processing (#2079)

hide bored waiting? button after request handling both success and error cases to properly hide the button.

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-22 09:14:26 +00:00
dependabot[bot]
6d3f14375e Bump bouncycastleVersion from 1.78.1 to 1.79 (#2177)
Bumps `bouncycastleVersion` from 1.78.1 to 1.79.

Updates `org.bouncycastle:bcprov-jdk18on` from 1.78.1 to 1.79
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

Updates `org.bouncycastle:bcpkix-jdk18on` from 1.78.1 to 1.79
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

---
updated-dependencies:
- dependency-name: org.bouncycastle:bcprov-jdk18on
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.bouncycastle:bcpkix-jdk18on
  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-11-22 09:06:30 +00:00
dependabot[bot]
888aec5701 Bump io.micrometer:micrometer-core from 1.13.6 to 1.14.1 (#2253)
Bumps [io.micrometer:micrometer-core](https://github.com/micrometer-metrics/micrometer) from 1.13.6 to 1.14.1.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.13.6...v1.14.1)

---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-core
  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-11-22 09:06:16 +00:00
dependabot[bot]
92e7e85e77 Bump gradle from 8.7-jdk17 to 8.11-jdk17 (#2269)
* Bump gradle from 8.7-jdk17 to 8.11-jdk17

Bumps gradle from 8.7-jdk17 to 8.11-jdk17.

---
updated-dependencies:
- dependency-name: gradle
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update gradle-wrapper.properties

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-22 09:02:22 +00:00
github-actions[bot]
3bf467e4ff 📝 Update README: Translation Progress Table (#2283)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-22 08:45:22 +00:00
yusif043-bit
dc1887db4d Translation az (#2287)
* Azerbaijani flag and dropdown item added

* Azerbaijani Language file Added

* AZ - ignore_translation.toml init

* AZ Translation Enterprise Edition Section

* Translation for Generic

* translation-az pipeline

* Translation for Analytics

* Translation for NAVBAR

* Translation for SETTINGS

* translation-az homepage

* Translation for #login

* Translation for (showJS)

* Translation for #showJS

* Translation for #PDFToWord

* Translation for #PDFToPresentation

* Translation for #PDFToText

* Translation for #PDFToHTML

* Translation for #PDFToXML

* Translation for #PDFToCSV

* Translation for #repair

* Translation for #pageLayout

* Translation for #pdfToSinglePage

* Translation for #pageExtracter

* Translation for #getPdfInfo

* Translation for #markdown-to-pdf

* Translation for #PDFToXML

* Translation for #html-to-pdf

* Translation for #PDFToHTML

* Translation for #PDFToText

* Translation for #PDFToPresentation

* Translation for #PDFToWord

* Translation for #PDFToCSV

* Translation for #url-to-pdf

* Translation for #pdfToImage

* Translation for #BookToPDF

* Translation for #PDFToBook

* Translation for #autoRedact

* Translation for #Add image

* Translation for #File to PDF

* Translation for (remove-image)

* Translation for (remove-image)

* Translation for (survey)

* Translation for (licenses)

* Translation for (printFile)

* Translation for (split-bysections)

* Translation for (overlay-pdfs)

* Translation for (split-by-size-or-count)

* Translation for (addPageNumbers)

* Translation for (adjustContrast)

* Translation for (autoSplitPDF)

* Translation for (scalePages)

* Translation for (removeCertSign)

* Translation for (removeAnnotations)

* Translation for (sign)

* Translation for (flatten)

* Translation for (extractImages)

* Translation for (merge)

* Translation for (view pdf)

* Translation for (pageRemover)

* Translation for (rotate)

* az Translation for replace-invert-color

* az Translation for addstamprequest

* Translation for (remove-image)

* Translation for #url-to-pdf

* Update messages_az_AZ.properties

* Update README.md

* Update README.md

* Update messages_az_AZ.properties

---------

Co-authored-by: NureddinFarzaliyev <nureddin.fa@gmail.com>
Co-authored-by: Lucifer25x <lucifer25x@protonmail.com>
Co-authored-by: islamd7 <vusal04999@gmail.com>
Co-authored-by: Valida Rahmanova <validerehmanova04@gmail.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-22 08:44:48 +00:00
albanobattistella
bab2052a60 Update messages_it_IT.properties (#2289)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-22 08:37:31 +00:00
Ludy
7773df7443 Fix: Convert a single string in the array to a list array (#2293) 2024-11-21 21:18:41 +00:00
Ludy
32d575b4e9 try to reduce the permission; update only translation files (#2291) 2024-11-21 20:00:12 +00:00
reecebrowne
4ebeedc028 Hover tools tooltips (#2290)
* Multi-tool advert on pages that share functionality

* Update translation files

Signed-off-by: GitHub Action <action@github.com>

* Rtl CSS

* Upgraded tooltips on multitool. Order selected pages list. Repositionicons. Minor additional tweaks

* restore gb translations

* Update translation files

Signed-off-by: GitHub Action <action@github.com>

* remove blankspace

* Restore hover tooltips

* Update translation files

Signed-off-by: GitHub Action <action@github.com>

---------

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-21 19:33:19 +00:00
reecebrowne
b4b005bc2e Feature/ux improvements (#2288)
* Multi-tool advert on pages that share functionality

* Update translation files

Signed-off-by: GitHub Action <action@github.com>

* Rtl CSS

* Upgraded tooltips on multitool. Order selected pages list. Repositionicons. Minor additional tweaks

* restore gb translations

* Update translation files

Signed-off-by: GitHub Action <action@github.com>

* remove blankspace

---------

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-21 17:34:50 +00:00
Omar Ahmed Hassan
b92bcfe915 Update Arabic language (#2282) 2024-11-21 14:54:05 +00:00
Omar Ahmed Hassan
aeca2b23d9 Fix: Expand and de-clutter menus for matching search results in homepage #2264 (#2277)
* Hide empty menus that don't match search criteria

- Hide empty menus (accordions/feature groups) that don't match search criteria.

* Expand menus automatically for matching search results

- Fix a bug where menus (accordions/feature groups) did not automatically expand on the homepage when search results matched.

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-21 14:24:45 +00:00
Anthony Stirling
68349c4426 Update downloader.js 2024-11-21 11:47:47 +00:00
Ludy
0f6d5e5a41 Note for PR creators added (#2279) 2024-11-21 11:31:32 +00:00
Ludy
df1c5476d9 Update German language (#2276) 2024-11-20 19:45:44 +00:00
Anthony Stirling
d0d6a70250 Metrics changes (#2273)
* Update downloader.js

* Update downloader.js

* Update common.html

* Update downloader.js
2024-11-20 10:32:44 +00:00
134 changed files with 6527 additions and 2327 deletions

View File

@@ -9,8 +9,9 @@ The script also provides functionality to update the translation files to match
adjusting the format.
Usage:
python script_name.py --reference-file <path_to_reference_file> --branch <branch_name> [--files <list_of_changed_files>]
python check_language_properties.py --reference-file <path_to_reference_file> --branch <branch_name> [--actor <actor_name>] [--files <list_of_changed_files>]
"""
import copy
import glob
import os
@@ -18,6 +19,10 @@ import argparse
import re
# Maximum size for properties files (e.g., 200 KB)
MAX_FILE_SIZE = 200 * 1024
def parse_properties_file(file_path):
"""Parses a .properties file and returns a list of objects (including comments, empty lines, and line numbers)."""
properties_list = []
@@ -95,7 +100,7 @@ def write_json_file(file_path, updated_properties):
def update_missing_keys(reference_file, file_list, branch=""):
reference_properties = parse_properties_file(reference_file)
for file_path in file_list:
basename_current_file = os.path.basename(branch + file_path)
basename_current_file = os.path.basename(os.path.join(branch, file_path))
if (
basename_current_file == os.path.basename(reference_file)
or not file_path.endswith(".properties")
@@ -103,7 +108,7 @@ def update_missing_keys(reference_file, file_list, branch=""):
):
continue
current_properties = parse_properties_file(branch + file_path)
current_properties = parse_properties_file(os.path.join(branch, file_path))
updated_properties = []
for ref_entry in reference_properties:
ref_entry_copy = copy.deepcopy(ref_entry)
@@ -114,60 +119,79 @@ def update_missing_keys(reference_file, file_list, branch=""):
if ref_entry_copy["key"] == current_entry["key"]:
ref_entry_copy["value"] = current_entry["value"]
updated_properties.append(ref_entry_copy)
write_json_file(branch + file_path, updated_properties)
write_json_file(os.path.join(branch, file_path), updated_properties)
def check_for_missing_keys(reference_file, file_list, branch):
update_missing_keys(reference_file, file_list, branch + "/")
update_missing_keys(reference_file, file_list, branch)
def read_properties(file_path):
with open(file_path, "r", encoding="utf-8") as file:
return file.read().splitlines()
if os.path.isfile(file_path) and os.path.exists(file_path):
with open(file_path, "r", encoding="utf-8") as file:
return file.read().splitlines()
return [""]
def check_for_differences(reference_file, file_list, branch):
def check_for_differences(reference_file, file_list, branch, actor):
reference_branch = reference_file.split("/")[0]
basename_reference_file = os.path.basename(reference_file)
report = []
report.append(
f"### 📋 Checking with the file `{basename_reference_file}` from the `{reference_branch}` - Checking the `{branch}`"
)
report.append(f"#### 🔄 Reference Branch: `{reference_branch}`")
reference_lines = read_properties(reference_file)
has_differences = False
only_reference_file = True
for file_path in file_list:
basename_current_file = os.path.basename(branch + "/" + file_path)
file_arr = file_list
if len(file_list) == 1:
file_arr = file_list[0].split()
base_dir = os.path.abspath(os.path.join(os.getcwd(), "src", "main", "resources"))
for file_path in file_arr:
absolute_path = os.path.abspath(file_path)
# Verify that file is within the expected directory
if not absolute_path.startswith(base_dir):
raise ValueError(f"Unsafe file found: {file_path}")
# Verify file size before processing
if os.path.getsize(os.path.join(branch, file_path)) > MAX_FILE_SIZE:
raise ValueError(
f"The file {file_path} is too large and could pose a security risk."
)
basename_current_file = os.path.basename(os.path.join(branch, file_path))
if (
basename_current_file == basename_reference_file
or not file_path.startswith(
os.path.join("src", "main", "resources", "messages_")
)
or not file_path.endswith(".properties")
or not basename_current_file.startswith("messages_")
):
continue
only_reference_file = False
report.append(f"#### 🗂️ **Checking File:** `{basename_current_file}`...")
current_lines = read_properties(branch + "/" + file_path)
report.append(f"#### 📃 **File Check:** `{basename_current_file}`")
current_lines = read_properties(os.path.join(branch, file_path))
reference_line_count = len(reference_lines)
current_line_count = len(current_lines)
if reference_line_count != current_line_count:
report.append("")
report.append("- **Test 1 Status:** ❌ Failed")
report.append("1. **Test Status:** ❌ **_Failed_**")
report.append(" - **Issue:**")
has_differences = True
if reference_line_count > current_line_count:
report.append(
f" - **Issue:** Missing lines! Comments, empty lines, or translation strings are missing. Details: {reference_line_count} (reference) vs {current_line_count} (current)."
f" - **_Mismatched line count_**: {reference_line_count} (reference) vs {current_line_count} (current). Comments, empty lines, or translation strings are missing."
)
elif reference_line_count < current_line_count:
report.append(
f" - **Issue:** Too many lines! Check your translation files! Details: {reference_line_count} (reference) vs {current_line_count} (current)."
f" - **_Too many lines_**: {reference_line_count} (reference) vs {current_line_count} (current). Please verify if there is an additional line that needs to be removed."
)
# update_missing_keys(reference_file, [file_path], branch + "/")
else:
report.append("- **Test 1 Status:** ✅ Passed")
report.append("1. **Test Status:** ✅ **_Passed_**")
# Check for missing or extra keys
current_keys = []
@@ -192,32 +216,42 @@ def check_for_differences(reference_file, file_list, branch):
has_differences = True
missing_keys_str = "`, `".join(missing_keys_list)
extra_keys_str = "`, `".join(extra_keys_list)
report.append("- **Test 2 Status:** ❌ Failed")
report.append("2. **Test Status:** ❌ **_Failed_**")
report.append(" - **Issue:**")
if missing_keys_list:
spaces_keys_list = []
for key in missing_keys_list:
if " " in key:
spaces_keys_list.append(key)
if spaces_keys_list:
spaces_keys_str = "`, `".join(spaces_keys_list)
report.append(
f" - **_Keys containing unnecessary spaces_**: `{spaces_keys_str}`!"
)
report.append(
f" - **Issue:** There are keys in ***{basename_current_file}*** `{missing_keys_str}` that are not present in ***{basename_reference_file}***!"
f" - **_Extra keys in `{basename_current_file}`_**: `{missing_keys_str}` that are not present in **_`{basename_reference_file}`_**."
)
if extra_keys_list:
report.append(
f" - **Issue:** There are keys in ***{basename_reference_file}*** `{extra_keys_str}` that are not present in ***{basename_current_file}***!"
f" - **_Missing keys in `{basename_reference_file}`_**: `{extra_keys_str}` that are not present in **_`{basename_current_file}`_**."
)
# update_missing_keys(reference_file, [file_path], branch + "/")
else:
report.append("- **Test 2 Status:** ✅ Passed")
# if has_differences:
# report.append("")
# report.append(f"#### 🚧 ***{basename_current_file}*** will be corrected...")
report.append("2. **Test Status:** ✅ **_Passed_**")
report.append("")
report.append("---")
report.append("")
# update_file_list = glob.glob(branch + "/src/**/messages_*.properties", recursive=True)
# update_missing_keys(reference_file, update_file_list)
# report.append("---")
# report.append("")
if has_differences:
report.append("## ❌ Overall Check Status: **_Failed_**")
report.append("")
report.append(
f"@{actor} please check your translation if it conforms to the standard. Follow the format of [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)"
)
else:
report.append("## ✅ Overall Check Status: **_Success_**")
report.append("")
report.append(
f"Thanks @{actor} for your help in keeping the translations up to date."
)
if not only_reference_file:
print("\n".join(report))
@@ -225,6 +259,11 @@ def check_for_differences(reference_file, file_list, branch):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Find missing keys")
parser.add_argument(
"--actor",
required=False,
help="Actor from PR.",
)
parser.add_argument(
"--reference-file",
required=True,
@@ -244,11 +283,21 @@ if __name__ == "__main__":
)
args = parser.parse_args()
# Sanitize --actor input to avoid injection attacks
if args.actor:
args.actor = re.sub(r"[^a-zA-Z0-9_\\-]", "", args.actor)
# Sanitize --branch input to avoid injection attacks
if args.branch:
args.branch = re.sub(r"[^a-zA-Z0-9\\-]", "", args.branch)
file_list = args.files
if file_list is None:
file_list = glob.glob(
os.getcwd() + "/src/**/messages_*.properties", recursive=True
os.path.join(
os.getcwd(), "src", "main", "resources", "messages_*.properties"
)
)
update_missing_keys(args.reference_file, file_list)
else:
check_for_differences(args.reference_file, file_list, args.branch)
check_for_differences(args.reference_file, file_list, args.branch, args.actor)

View File

@@ -6,14 +6,10 @@ on:
paths:
- "src/main/resources/messages_*.properties"
push:
branches: ["main"]
paths:
- "src/main/resources/messages_en_GB.properties"
# Permissions required for the workflow
permissions:
contents: write
pull-requests: write
jobs:
check-files:
if: github.event_name == 'pull_request_target'
@@ -54,10 +50,7 @@ jobs:
echo "Getting list of changed files from PR..."
gh pr view ${{ github.event.pull_request.number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > ../changed_files.txt
cd ..
echo "Setting branch path..."
BRANCH_PATH="pr-branch"
echo "BRANCH_PATH=${BRANCH_PATH}" >> $GITHUB_ENV
echo "Processing changed files..."
mapfile -t CHANGED_FILES < changed_files.txt
@@ -65,7 +58,6 @@ jobs:
echo "CHANGED_FILES=${CHANGED_FILES_STR}" >> $GITHUB_ENV
echo "Changed files: ${CHANGED_FILES_STR}"
echo "Branch: ${BRANCH_PATH}"
- name: Determine reference file
id: determine-file
@@ -87,31 +79,40 @@ jobs:
run: |
echo "Running Python script to check files..."
python main-branch/.github/scripts/check_language_properties.py \
--actor ${{ github.event.pull_request.user.login }} \
--reference-file "${REFERENCE_FILE}" \
--branch "${BRANCH_PATH}" \
--files ${CHANGED_FILES} > failure.txt || true
--branch pr-branch \
--files "${CHANGED_FILES[@]}" > result.txt || true
- name: Capture output
id: capture-output
run: |
if [ -f failure.txt ] && [ -s failure.txt ]; then
echo "Test failed, capturing output..."
ERROR_OUTPUT=$(cat failure.txt)
echo "ERROR_OUTPUT<<EOF" >> $GITHUB_ENV
echo "$ERROR_OUTPUT" >> $GITHUB_ENV
if [ -f result.txt ] && [ -s result.txt ]; then
echo "Test, capturing output..."
SCRIPT_OUTPUT=$(cat result.txt)
echo "SCRIPT_OUTPUT<<EOF" >> $GITHUB_ENV
echo "$SCRIPT_OUTPUT" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "${ERROR_OUTPUT}"
echo "${SCRIPT_OUTPUT}"
# Set FAIL_JOB to true if SCRIPT_OUTPUT contains ❌
if [[ "$SCRIPT_OUTPUT" == *"❌"* ]]; then
echo "FAIL_JOB=true" >> $GITHUB_ENV
else
echo "FAIL_JOB=false" >> $GITHUB_ENV
fi
else
echo "No errors found."
echo "ERROR_OUTPUT=" >> $GITHUB_ENV
echo "No update found."
echo "SCRIPT_OUTPUT=" >> $GITHUB_ENV
echo "FAIL_JOB=false" >> $GITHUB_ENV
fi
- name: Post comment on PR
if: env.ERROR_OUTPUT != ''
if: env.SCRIPT_OUTPUT != ''
uses: actions/github-script@v7
with:
script: |
const { GITHUB_REPOSITORY, ERROR_OUTPUT } = process.env;
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
const prNumber = context.issue.number;
@@ -123,7 +124,7 @@ jobs:
});
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
// Only allow the action user to update comments
const expectedActor = "github-actions[bot]";
@@ -133,7 +134,7 @@ jobs:
owner: repoOwner,
repo: repoName,
comment_id: comment.id,
body: `## 🚀 Translation Verification Summary\n\n\n${ERROR_OUTPUT}\n`
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
});
console.log("Updated existing comment.");
} else if (!comment) {
@@ -142,33 +143,24 @@ jobs:
owner: repoOwner,
repo: repoName,
issue_number: prNumber,
body: `## 🚀 Translation Verification Summary\n\n\n${ERROR_OUTPUT}\n`
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
});
console.log("Created new comment.");
} else {
console.log("Comment update attempt denied. Actor does not match.");
}
# - name: Set up git config
# run: |
# git config --global user.name "github-actions[bot]"
# git config --global user.email "github-actions[bot]@users.noreply.github.com"
# - name: Add translation keys
# run: |
# cd ${{ env.BRANCH_PATH }}
# git add src/main/resources/messages_*.properties
# git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
# git commit -m "Update translation files" || echo "No changes to commit"
# - name: Push
# if: env.CHANGES_DETECTED == 'true'
# run: |
# cd pr-branch
# git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.event.pull_request.head.repo.full_name }}.git
# git push origin ${{ github.head_ref }} || echo "Push failed: possibly no changes to push"
- name: Fail job if errors found
if: env.FAIL_JOB == 'true'
run: |
echo "Failing the job because errors were detected."
exit 1
update-translations-main:
if: github.event_name == 'push'
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@@ -200,7 +192,7 @@ jobs:
- name: Create Pull Request
id: cpr
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@v6
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Update translation files"
@@ -209,6 +201,8 @@ jobs:
signoff: true
branch: update_translation_files
title: "Update translation files"
add-paths: |
src/main/resources/messages_*.properties
body: |
Auto-generated by [create-pull-request][1]
@@ -216,3 +210,4 @@ jobs:
labels: Translation
draft: false
delete-branch: true
sign-commits: true

View File

@@ -11,7 +11,7 @@ Stirling-PDF is built using:
- Spring Boot + Thymeleaf
- PDFBox
- LibreOffice
- OcrMyPdf
- qpdf
- HTML, CSS, JavaScript
- Docker
- PDF.js
@@ -243,7 +243,7 @@ To run Stirling-PDF locally:
Important notes:
- Local testing doesn't include features that depend on external tools like OCRmyPDF, LibreOffice, or Python scripts.
- Local testing doesn't include features that depend on external tools like qpdf, LibreOffice, or Python scripts.
- There are currently no automated unit tests. All testing is done manually through the UI or API calls. (You are welcome to add JUnits!)
- Always verify your changes in the full Docker environment before submitting pull requests, as some integrations and features will only work in the complete setup.

View File

@@ -30,6 +30,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
tini \
bash \
curl \
qpdf \
shadow \
su-exec \
openssl \
@@ -40,7 +41,6 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced features)
ocrmypdf \
tesseract-ocr-data-eng \
# CV
py3-opencv \

View File

@@ -1,5 +1,5 @@
# Build the application
FROM gradle:8.7-jdk17 AS build
FROM gradle:8.11-jdk17 AS build
# Set the working directory
WORKDIR /app
@@ -55,7 +55,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced featues)
ocrmypdf \
qpdf \
tesseract-ocr-data-eng \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \
# CV

View File

@@ -1,4 +1,4 @@
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript | Unoconv | Ghostscript |
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | qpdf | Java | Javascript | Unoconv | tesseract |
| ------------------- | ------- | ------- | -------- | ----- | --- | ------ | ------ | ----------- | -------- | ---- | ---------- | ------- | ----------- |
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ | | |
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | | | |
@@ -16,7 +16,7 @@
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | | | |
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | | | |
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | ✔️ |
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | |
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | | | |
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
@@ -34,13 +34,13 @@
| auto-rename | | | | ✔️ | | | | | | ✔️ | | | |
| change-metadata | | | | ✔️ | | | | | | ✔️ | | | |
| compare | | | | ✔️ | | | | | | | ✔️ | | |
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | ✔️ |
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | |
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
| extract-images | | | | ✔️ | | | | | | ✔️ | | | |
| flatten | | | | ✔️ | | | | | | | ✔️ | | |
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | | | |
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | |
| ocr-pdf | | | | ✔️ | ✔️ | | | | | | | | |
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | | | ✔️ |
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | | | |
| show-javascript | | | | ✔️ | | | | | | | ✔️ | | |
| sign | | | | ✔️ | | | | | | | ✔️ | | |

View File

@@ -8,7 +8,7 @@ The paths have changed for the tessdata locations on new Docker images. Please u
## How does the OCR Work
Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF), which in turn uses Tesseract for its text recognition. All credit goes to them for this awesome work!
Stirling-PDF uses Tesseract for its text recognition. All credit goes to them for this awesome work!
## Language Packs
@@ -52,8 +52,6 @@ Add the following to your existing Docker run command:
### Non-Docker Setup
If you are not using Docker, you need to install the OCR components, including the `ocrmypdf` app. You can see the [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html).
For Debian-based systems, install languages with this command:
```bash
@@ -83,8 +81,7 @@ rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'
For Windows:
Ensure ocrmypdf in installed with
``pip install ocrmypdf``
You must ensure tesseract is installed
Additional languages must be downloaded manually:
Download desired .traineddata files from tessdata or tessdata_fast

View File

@@ -68,7 +68,7 @@ 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.
Next we need to install LibreOffice for conversions, qpdf for OCR, and OpenCV for pattern recognition functionality.
Install the following software:
@@ -81,27 +81,27 @@ Install the following software:
- unoconv
- pngquant
- unpaper
- ocrmypdf
- qpdf
- opencv-python-headless
For Debian-based systems, you can use the following command:
```bash
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages
```
For Fedora:
```bash
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
```
For Nix:
```bash
nix-env -iA nixpkgs.unpaper nixpkgs.libreoffice nixpkgs.ocrmypdf nixpkgs.poppler_utils
nix-env -iA nixpkgs.unpaper nixpkgs.libreoffice nixpkgs.qpdf nixpkgs.poppler_utils
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
```
@@ -146,7 +146,6 @@ The easiest method is to use the language packs provided by your repositories. S
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.
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.

View File

@@ -6,6 +6,7 @@
[![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)
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling&#0045;pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling&#0032;PDF - Open&#0032;source&#0032;locally&#0032;hosted&#0032;web&#0032;PDF&#0032;editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
[![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)
[Stirling-PDF](https://www.stirlingpdf.com) 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.
@@ -78,15 +79,15 @@ All files and PDFs exist either exclusively on the client side, reside in server
- Detect and remove blank pages
- Compare two PDFs and show differences in text
- Add images to PDFs
- Compress PDFs to decrease their filesize (using OCRMyPDF)
- Compress PDFs to decrease their filesize (using qpdf)
- Extract images from PDF
- Remove images from PDF
- Extract images from scans
- Remove annotations
- Add page numbers
- Auto rename file by detecting PDF header text
- OCR on PDF (using OCRMyPDF)
- PDF/A conversion (using OCRMyPDF)
- OCR on PDF (using tesseract)
- PDF/A conversion (using libreoffice)
- Edit metadata
- Flatten PDFs
- Get all information on a PDF to view or export as JSON
@@ -101,7 +102,7 @@ A demo of the app is available [here](https://stirlingpdf.io).
- Spring Boot + Thymeleaf
- [PDFBox](https://github.com/apache/pdfbox/tree/trunk)
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
- [OcrMyPdf](https://github.com/ocrmypdf/OCRmyPDF)
- [qpdf](https://github.com/qpdf/qpdf)
- HTML, CSS, JavaScript
- Docker
- [PDF.js](https://github.com/mozilla/pdf.js)
@@ -186,46 +187,47 @@ Certain functionality like `Sign` supports pre-saved files stored at `/customFil
## Supported Languages
Stirling-PDF currently supports 36 languages!
Stirling-PDF currently supports 37 languages!
| Language | Progress |
| -------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![97%](https://geps.dev/progress/97) |
| Basque (Euskara) (eu_ES) | ![55%](https://geps.dev/progress/55) |
| Bulgarian (Български) (bg_BG) | ![96%](https://geps.dev/progress/96) |
| Catalan (Català) (ca_CA) | ![90%](https://geps.dev/progress/90) |
| Croatian (Hrvatski) (hr_HR) | ![98%](https://geps.dev/progress/98) |
| Czech (Česky) (cs_CZ) | ![97%](https://geps.dev/progress/97) |
| Danish (Dansk) (da_DK) | ![96%](https://geps.dev/progress/96) |
| Dutch (Nederlands) (nl_NL) | ![96%](https://geps.dev/progress/96) |
| Arabic (العربية) (ar_AR) | ![98%](https://geps.dev/progress/98) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![97%](https://geps.dev/progress/97) |
| Basque (Euskara) (eu_ES) | ![54%](https://geps.dev/progress/54) |
| Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Catalan (Català) (ca_CA) | ![88%](https://geps.dev/progress/88) |
| Croatian (Hrvatski) (hr_HR) | ![95%](https://geps.dev/progress/95) |
| Czech (Česky) (cs_CZ) | ![95%](https://geps.dev/progress/95) |
| Danish (Dansk) (da_DK) | ![94%](https://geps.dev/progress/94) |
| Dutch (Nederlands) (nl_NL) | ![93%](https://geps.dev/progress/93) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![97%](https://geps.dev/progress/97) |
| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) |
| Greek (Ελληνικά) (el_GR) | ![97%](https://geps.dev/progress/97) |
| Hindi (हिंदी) (hi_IN) | ![95%](https://geps.dev/progress/95) |
| Hungarian (Magyar) (hu_HU) | ![98%](https://geps.dev/progress/98) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![97%](https://geps.dev/progress/97) |
| Irish (Gaeilge) (ga_IE) | ![88%](https://geps.dev/progress/88) |
| German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
| Greek (Ελληνικά) (el_GR) | ![95%](https://geps.dev/progress/95) |
| Hindi (हिंदी) (hi_IN) | ![92%](https://geps.dev/progress/92) |
| Hungarian (Magyar) (hu_HU) | ![95%](https://geps.dev/progress/95) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![95%](https://geps.dev/progress/95) |
| Irish (Gaeilge) (ga_IE) | ![85%](https://geps.dev/progress/85) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![85%](https://geps.dev/progress/85) |
| Korean (한국어) (ko_KR) | ![95%](https://geps.dev/progress/95) |
| Norwegian (Norsk) (no_NB) | ![87%](https://geps.dev/progress/87) |
| Polish (Polski) (pl_PL) | ![97%](https://geps.dev/progress/97) |
| Portuguese (Português) (pt_PT) | ![97%](https://geps.dev/progress/97) |
| Portuguese Brazilian (Português) (pt_BR) | ![98%](https://geps.dev/progress/98) |
| Romanian (Română) (ro_RO) | ![90%](https://geps.dev/progress/90) |
| Russian (Русский) (ru_RU) | ![97%](https://geps.dev/progress/97) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![70%](https://geps.dev/progress/70) |
| Simplified Chinese (简体中文) (zh_CN) | ![91%](https://geps.dev/progress/91) |
| Slovakian (Slovensky) (sk_SK) | ![82%](https://geps.dev/progress/82) |
| Spanish (Español) (es_ES) | ![98%](https://geps.dev/progress/98) |
| Swedish (Svenska) (sv_SE) | ![97%](https://geps.dev/progress/97) |
| Thai (ไทย) (th_TH) | ![96%](https://geps.dev/progress/96) |
| Traditional Chinese (繁體中文) (zh_TW) | ![98%](https://geps.dev/progress/98) |
| Turkish (Türkçe) (tr_TR) | ![92%](https://geps.dev/progress/92) |
| Ukrainian (Українська) (uk_UA) | ![80%](https://geps.dev/progress/80) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![88%](https://geps.dev/progress/88) |
| Japanese (日本語) (ja_JP) | ![83%](https://geps.dev/progress/83) |
| Korean (한국어) (ko_KR) | ![93%](https://geps.dev/progress/93) |
| Norwegian (Norsk) (no_NB) | ![85%](https://geps.dev/progress/85) |
| Polish (Polski) (pl_PL) | ![94%](https://geps.dev/progress/94) |
| Portuguese (Português) (pt_PT) | ![95%](https://geps.dev/progress/95) |
| Portuguese Brazilian (Português) (pt_BR) | ![95%](https://geps.dev/progress/95) |
| Romanian (Română) (ro_RO) | ![87%](https://geps.dev/progress/87) |
| Russian (Русский) (ru_RU) | ![94%](https://geps.dev/progress/94) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![68%](https://geps.dev/progress/68) |
| Simplified Chinese (简体中文) (zh_CN) | ![89%](https://geps.dev/progress/89) |
| Slovakian (Slovensky) (sk_SK) | ![80%](https://geps.dev/progress/80) |
| Spanish (Español) (es_ES) | ![95%](https://geps.dev/progress/95) |
| Swedish (Svenska) (sv_SE) | ![94%](https://geps.dev/progress/94) |
| Thai (ไทย) (th_TH) | ![93%](https://geps.dev/progress/93) |
| Traditional Chinese (繁體中文) (zh_TW) | ![95%](https://geps.dev/progress/95) |
| Turkish (Türkçe) (tr_TR) | ![89%](https://geps.dev/progress/89) |
| Ukrainian (Українська) (uk_UA) | ![78%](https://geps.dev/progress/78) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![86%](https://geps.dev/progress/86) |
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)

View File

@@ -8,7 +8,7 @@ The 'Fat' container contains all those found in 'Full' with security jar along w
| Libre | | ✔️ |
| Python | | ✔️ |
| OpenCV | | ✔️ |
| OCRmyPDF | | ✔️ |
| qpdf | | ✔️ |
| Operation | Ultra-Lite | Full |
| ---------------------- | ---------- | ---- |

View File

@@ -1,6 +1,6 @@
plugins {
id "java"
id "org.springframework.boot" version "3.3.5"
id "org.springframework.boot" version "3.4.0"
id "io.spring.dependency-management" version "1.1.6"
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
id "io.swagger.swaggerhub" version "1.3.2"
@@ -15,16 +15,19 @@ plugins {
import com.github.jk1.license.render.*
ext {
springBootVersion = "3.3.5"
springBootVersion = "3.4.0"
pdfboxVersion = "3.0.3"
logbackVersion = "1.5.7"
imageioVersion = "3.12.0"
lombokVersion = "1.18.36"
bouncycastleVersion = "1.78.1"
bouncycastleVersion = "1.79"
springSecuritySamlVersion = "6.4.1"
openSamlVersion = "4.3.2"
}
group = "stirling.software"
version = "0.33.1"
version = "0.35.1"
java {
// 17 is lowest but we support and recommend 21
@@ -121,7 +124,7 @@ configurations.all {
}
dependencies {
//security updates
implementation "org.springframework:spring-webmvc:6.1.14"
implementation "org.springframework:spring-webmvc:6.2.0"
implementation("io.github.pixee:java-security-toolkit:1.2.0")
@@ -143,18 +146,18 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
implementation 'org.springframework.security:spring-security-saml2-service-provider:6.3.4'
implementation "org.springframework.session:spring-session-core:$springBootVersion"
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
//2.2.x requires rebuild of DB file.. need migration path
runtimeOnly "com.h2database:h2:2.1.214"
// implementation "com.h2database:h2:2.2.224"
// Don't upgrade h2database
runtimeOnly "com.h2database:h2:2.3.232"
constraints {
implementation "org.opensaml:opensaml-core"
implementation "org.opensaml:opensaml-saml-api"
implementation "org.opensaml:opensaml-saml-impl"
implementation "org.opensaml:opensaml-core:$openSamlVersion"
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
}
implementation "org.springframework.security:spring-security-saml2-service-provider"
implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
// implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion'
implementation 'com.coveo:saml-client:5.0.0'
@@ -186,7 +189,7 @@ dependencies {
// Image metadata extractor
implementation "com.drewnoakes:metadata-extractor:2.19.0"
implementation "commons-io:commons-io:2.17.0"
implementation "commons-io:commons-io:2.18.0"
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0"
//general PDF
@@ -203,12 +206,19 @@ dependencies {
exclude group: "commons-logging", module: "commons-logging"
}
// https://mvnrepository.com/artifact/technology.tabula/tabula
implementation ('technology.tabula:tabula:1.0.5') {
exclude group: "org.slf4j", module: "slf4j-simple"
exclude group: "org.bouncycastle", module: "bcprov-jdk15on"
exclude group: "com.google.code.gson", module: "gson"
}
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4'
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
implementation "io.micrometer:micrometer-core:1.13.6"
implementation "io.micrometer:micrometer-core:1.14.1"
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark:0.24.0"

View File

@@ -48,24 +48,6 @@ Feature: API Validation
And the response status code should be 200
@ocr @negative
Scenario: Process PDF with text and OCR with type normal
Given I generate a PDF file as "fileInput"
And the pdf contains 3 pages with random text
And the request data includes
| parameter | value |
| languages | eng |
| sidecar | false |
| deskew | true |
| clean | true |
| cleanFinal | true |
| ocrType | Normal |
| ocrRenderType | hocr |
| removeImagesAfter| false |
When I send the API request to the endpoint "/api/v1/misc/ocr-pdf"
Then the response status code should be 500
@ocr @positive
Scenario: Process PDF with OCR
Given I generate a PDF file as "fileInput"
@@ -83,26 +65,6 @@ Feature: API Validation
Then the response content type should be "application/pdf"
And the response file should have size greater than 0
And the response status code should be 200
@ocr @positive
Scenario: Process PDF with OCR with sidecar
Given I generate a PDF file as "fileInput"
And the request data includes
| parameter | value |
| languages | eng |
| sidecar | true |
| deskew | true |
| clean | true |
| cleanFinal | true |
| ocrType | Force |
| ocrRenderType | hocr |
| removeImagesAfter| false |
When I send the API request to the endpoint "/api/v1/misc/ocr-pdf"
Then the response content type should be "application/octet-stream"
And the response file should have extension ".zip"
And the response ZIP should contain 2 files
And the response file should have size greater than 0
And the response status code should be 200
@libre @positive
@@ -145,7 +107,7 @@ Feature: API Validation
And the response file should have extension ".pdf"
And the response file should have size greater than 100
@compress @ghostscript @positive
@compress @qpdf @positive
Scenario: Compress
Given I use an example file at "exampleFiles/ghost3.pdf" as parameter "fileInput"
And the request data includes
@@ -156,7 +118,7 @@ Feature: API Validation
And the response file should have extension ".pdf"
And the response file should have size greater than 100
@compress @ghostscript @positive
@compress @qpdf @positive
Scenario: Compress
Given I use an example file at "exampleFiles/ghost2.pdf" as parameter "fileInput"
And the request data includes
@@ -169,7 +131,7 @@ Feature: API Validation
And the response file should have size greater than 100
@compress @ghostscript @positive
@compress @qpdf @positive
Scenario: Compress
Given I use an example file at "exampleFiles/ghost1.pdf" as parameter "fileInput"
And the request data includes

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -3,6 +3,11 @@ ignore = [
'language.direction',
]
[az_AZ]
ignore = [
'language.direction',
]
[bg_BG]
ignore = [
'language.direction',

View File

@@ -1,8 +1,8 @@
#!/bin/bash
translation_key="pdfToPDFA.credit"
old_value="OCRmyPDF"
new_value="ghostscript"
old_value="qpdf"
new_value="liibreoffice"
for file in ../src/main/resources/messages_*.properties; do
sed -i "/^$translation_key=/s/$old_value/$new_value/" "$file"

View File

@@ -3,13 +3,14 @@ package stirling.software.SPDF.EE;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
@Configuration
@Lazy
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class EEAppConfig {

View File

@@ -25,9 +25,10 @@ public class LicenseKeyChecker {
KeygenLicenseVerifier licenseService, ApplicationProperties applicationProperties) {
this.licenseService = licenseService;
this.applicationProperties = applicationProperties;
this.checkLicense();
}
@Scheduled(fixedRate = 604800000, initialDelay = 1000) // 7 days in milliseconds
@Scheduled(fixedRate = 604800000) // 7 days in milliseconds
public void checkLicensePeriodically() {
checkLicense();
}

View File

@@ -188,7 +188,7 @@ public class EndpointConfiguration {
addEndpointToGroup("OpenCV", "extract-image-scans");
// LibreOffice
addEndpointToGroup("LibreOffice", "repair");
addEndpointToGroup("qpdf", "repair");
addEndpointToGroup("LibreOffice", "file-to-pdf");
addEndpointToGroup("LibreOffice", "pdf-to-word");
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
@@ -199,10 +199,11 @@ public class EndpointConfiguration {
// Unoconv
addEndpointToGroup("Unoconv", "file-to-pdf");
// OCRmyPDF
addEndpointToGroup("OCRmyPDF", "compress-pdf");
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa");
addEndpointToGroup("OCRmyPDF", "ocr-pdf");
// qpdf
addEndpointToGroup("qpdf", "compress-pdf");
addEndpointToGroup("qpdf", "pdf-to-pdfa");
addEndpointToGroup("tesseract", "ocr-pdf");
// Java
addEndpointToGroup("Java", "merge-pdfs");
@@ -248,10 +249,10 @@ public class EndpointConfiguration {
addEndpointToGroup("Javascript", "compare");
addEndpointToGroup("Javascript", "adjust-contrast");
// Ghostscript dependent endpoints
addEndpointToGroup("Ghostscript", "compress-pdf");
addEndpointToGroup("Ghostscript", "pdf-to-pdfa");
addEndpointToGroup("Ghostscript", "repair");
// qpdf dependent endpoints
addEndpointToGroup("qpdf", "compress-pdf");
addEndpointToGroup("qpdf", "pdf-to-pdfa");
addEndpointToGroup("qpdf", "repair");
// Weasyprint dependent endpoints
addEndpointToGroup("Weasyprint", "html-to-pdf");

View File

@@ -37,12 +37,12 @@ public class ExternalAppDepConfig {
private final Map<String, List<String>> commandToGroupMapping =
new HashMap<>() {
{
put("gs", List.of("Ghostscript"));
put("soffice", List.of("LibreOffice"));
put("ocrmypdf", List.of("OCRmyPDF"));
put("weasyprint", List.of("Weasyprint"));
put("pdftohtml", List.of("Pdftohtml"));
put("unoconv", List.of("Unoconv"));
put("qpdf", List.of("qpdf"));
put("tesseract", List.of("tesseract"));
}
};
@@ -97,9 +97,9 @@ public class ExternalAppDepConfig {
public void checkDependencies() {
// Check core dependencies
checkDependencyAndDisableGroup("gs");
checkDependencyAndDisableGroup("tesseract");
checkDependencyAndDisableGroup("soffice");
checkDependencyAndDisableGroup("ocrmypdf");
checkDependencyAndDisableGroup("qpdf");
checkDependencyAndDisableGroup("weasyprint");
checkDependencyAndDisableGroup("pdftohtml");
checkDependencyAndDisableGroup("unoconv");

View File

@@ -30,6 +30,7 @@ public class InitialSecuritySetup {
initializeAdminUser();
} else {
databaseBackupHelper.exportDatabase();
userService.migrateOauth2ToSSO();
}
initializeInternalApiUser();
}

View File

@@ -1,16 +1,20 @@
package stirling.software.SPDF.config.security;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.*;
import org.opensaml.saml.saml2.core.AuthnRequest;
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.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -28,19 +32,35 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
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.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.context.SecurityContextHolderFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
@@ -64,6 +84,7 @@ import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@EnableWebSecurity
@EnableMethodSecurity
@Slf4j
@DependsOn("runningEE")
public class SecurityConfiguration {
@Autowired private CustomUserDetailsService userDetailsService;
@@ -79,6 +100,10 @@ public class SecurityConfiguration {
@Qualifier("loginEnabled")
public boolean loginEnabledValue;
@Autowired
@Qualifier("runningEE")
public boolean runningEE;
@Autowired ApplicationProperties applicationProperties;
@Autowired private UserAuthenticationFilter userAuthenticationFilter;
@@ -90,13 +115,14 @@ public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
if (applicationProperties.getSecurity().getCsrfDisabled()) {
http.csrf(csrf -> csrf.disable());
}
if (loginEnabledValue) {
http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
if (applicationProperties.getSecurity().getCsrfDisabled()) {
http.csrf(csrf -> csrf.disable());
} else {
if (!applicationProperties.getSecurity().getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo =
CookieCsrfTokenRepository.withHttpOnlyFalse();
CsrfTokenRequestAttributeHandler requestHandler =
@@ -137,7 +163,7 @@ public class SecurityConfiguration {
http.sessionManagement(
sessionManagement ->
sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(10)
.maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry)
@@ -245,12 +271,23 @@ public class SecurityConfiguration {
}
// Handle SAML
if (applicationProperties.getSecurity().isSaml2Activ()
&& applicationProperties.getSystem().getEnableAlphaFunctionality()) {
http.authenticationProvider(samlAuthenticationProvider());
http.saml2Login(
saml2 ->
if (applicationProperties.getSecurity().isSaml2Activ()) { // && runningEE
// Configure the authentication provider
OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider();
authenticationProvider.setResponseAuthenticationConverter(
new CustomSaml2ResponseAuthenticationConverter(userService));
http.authenticationProvider(authenticationProvider)
.saml2Login(
saml2 -> {
try {
saml2.loginPage("/saml2")
.relyingPartyRegistrationRepository(
relyingPartyRegistrations())
.authenticationManager(
new ProviderManager(authenticationProvider))
.successHandler(
new CustomSaml2AuthenticationSuccessHandler(
loginAttemptService,
@@ -258,14 +295,18 @@ public class SecurityConfiguration {
userService))
.failureHandler(
new CustomSaml2AuthenticationFailureHandler())
.permitAll())
.addFilterBefore(
userAuthenticationFilter, Saml2WebSsoAuthenticationFilter.class);
.authenticationRequestResolver(
authenticationRequestResolver(
relyingPartyRegistrations()));
} catch (Exception e) {
log.error("Error configuring SAML2 login", e);
throw new RuntimeException(e);
}
});
}
} else {
if (applicationProperties.getSecurity().getCsrfDisabled()) {
http.csrf(csrf -> csrf.disable());
} else {
if (!applicationProperties.getSecurity().getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo =
CookieCsrfTokenRepository.withHttpOnlyFalse();
CsrfTokenRequestAttributeHandler requestHandler =
@@ -282,20 +323,6 @@ public class SecurityConfiguration {
return http.build();
}
@Bean
@ConditionalOnProperty(
name = "security.saml2.enabled",
havingValue = "true",
matchIfMissing = false)
public AuthenticationProvider samlAuthenticationProvider() {
OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider();
authenticationProvider.setResponseAuthenticationConverter(
new CustomSaml2ResponseAuthenticationConverter(userService));
return authenticationProvider;
}
// Client Registration Repository for OAUTH2 OIDC Login
@Bean
@ConditionalOnProperty(
value = "security.oauth2.enabled",
@@ -425,18 +452,19 @@ public class SecurityConfiguration {
.clientName("OIDC")
.build());
}
@Bean
@ConditionalOnProperty(
name = "security.saml2.enabled",
havingValue = "true",
matchIfMissing = false)
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
Resource privateKeyResource = samlConf.getPrivateKey();
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
Resource privateKeyResource = samlConf.getPrivateKey();
Resource certificateResource = samlConf.getSpCert();
Saml2X509Credential signingCredential =
@@ -445,26 +473,97 @@ public class SecurityConfiguration {
CertificateUtils.readCertificate(certificateResource),
Saml2X509CredentialType.SIGNING);
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
RelyingPartyRegistration rp =
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
.signingX509Credentials((c) -> c.add(signingCredential))
.assertingPartyDetails(
(details) ->
details.entityId(samlConf.getIdpIssuer())
.signingX509Credentials(c -> c.add(signingCredential))
.assertingPartyMetadata(
metadata ->
metadata.entityId(samlConf.getIdpIssuer())
.singleSignOnServiceLocation(
samlConf.getIdpSingleLoginUrl())
.verificationX509Credentials(
(c) -> c.add(verificationCredential))
c -> c.add(verificationCredential))
.singleSignOnServiceBinding(
Saml2MessageBinding.POST)
.wantAuthnRequestsSigned(true))
.build();
return new InMemoryRelyingPartyRegistrationRepository(rp);
}
@Bean
@ConditionalOnProperty(
name = "security.saml2.enabled",
havingValue = "true",
matchIfMissing = false)
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
OpenSaml4AuthenticationRequestResolver resolver =
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
resolver.setAuthnRequestCustomizer(
customizer -> {
log.debug("Customizing SAML Authentication request");
AuthnRequest authnRequest = customizer.getAuthnRequest();
log.debug("AuthnRequest ID: {}", authnRequest.getID());
if (authnRequest.getID() == null) {
authnRequest.setID("ARQ" + UUID.randomUUID().toString());
}
log.debug("AuthnRequest new ID after set: {}", authnRequest.getID());
log.debug("AuthnRequest IssueInstant: {}", authnRequest.getIssueInstant());
log.debug(
"AuthnRequest Issuer: {}",
authnRequest.getIssuer() != null
? authnRequest.getIssuer().getValue()
: "null");
HttpServletRequest request = customizer.getRequest();
// Log HTTP request details
log.debug("HTTP Request Method: {}", request.getMethod());
log.debug("Request URI: {}", request.getRequestURI());
log.debug("Request URL: {}", request.getRequestURL().toString());
log.debug("Query String: {}", request.getQueryString());
log.debug("Remote Address: {}", request.getRemoteAddr());
// Log headers
Collections.list(request.getHeaderNames())
.forEach(
headerName -> {
log.debug(
"Header - {}: {}",
headerName,
request.getHeader(headerName));
});
// Log SAML specific parameters
log.debug("SAML Request Parameters:");
log.debug("SAMLRequest: {}", request.getParameter("SAMLRequest"));
log.debug("RelayState: {}", request.getParameter("RelayState"));
// Log session debugrmation if exists
if (request.getSession(false) != null) {
log.debug("Session ID: {}", request.getSession().getId());
}
// Log any assertions consumer service details if present
if (authnRequest.getAssertionConsumerServiceURL() != null) {
log.debug(
"AssertionConsumerServiceURL: {}",
authnRequest.getAssertionConsumerServiceURL());
}
// Log NameID policy if present
if (authnRequest.getNameIDPolicy() != null) {
log.debug(
"NameIDPolicy Format: {}",
authnRequest.getNameIDPolicy().getFormat());
}
});
return resolver;
}
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);

View File

@@ -18,6 +18,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
@@ -50,8 +51,19 @@ public class UserService implements UserServiceInterface {
@Autowired ApplicationProperties applicationProperties;
@Transactional
public void migrateOauth2ToSSO() {
userRepository
.findByAuthenticationTypeIgnoreCase("OAUTH2")
.forEach(
user -> {
user.setAuthenticationType(AuthenticationType.SSO);
userRepository.save(user);
});
}
// Handle OAUTH2 login and user auto creation.
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser)
public boolean processSSOPostLogin(String username, boolean autoCreateUser)
throws IllegalArgumentException, IOException {
if (!isUsernameValid(username)) {
return false;
@@ -61,7 +73,7 @@ public class UserService implements UserServiceInterface {
return true;
}
if (autoCreateUser) {
saveUser(username, AuthenticationType.OAUTH2);
saveUser(username, AuthenticationType.SSO);
return true;
}
return false;

View File

@@ -82,8 +82,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
}
if (userService.usernameExistsIgnoreCase(username)
&& userService.hasPassword(username)
&& !userService.isAuthenticationTypeByUsername(
username, AuthenticationType.OAUTH2)
&& !userService.isAuthenticationTypeByUsername(username, AuthenticationType.SSO)
&& oAuth.getAutoCreateUser()) {
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
return;
@@ -95,7 +94,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
return;
}
if (principal instanceof OAuth2User) {
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
userService.processSSOPostLogin(username, oAuth.getAutoCreateUser());
}
response.sendRedirect(contextPath + "/");
return;

View File

@@ -3,12 +3,14 @@ package stirling.software.SPDF.config.security.saml2;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.core.io.Resource;
@@ -28,15 +30,26 @@ public class CertificateUtils {
}
public static RSAPrivateKey readPrivateKey(Resource privateKeyResource) throws Exception {
try (PemReader pemReader =
new PemReader(
try (PEMParser pemParser =
new PEMParser(
new InputStreamReader(
privateKeyResource.getInputStream(), StandardCharsets.UTF_8))) {
PemObject pemObject = pemReader.readPemObject();
byte[] decodedKey = pemObject.getContent();
return (RSAPrivateKey)
KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(decodedKey));
Object object = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
if (object instanceof PEMKeyPair) {
// Handle traditional RSA private key format
PEMKeyPair keypair = (PEMKeyPair) object;
return (RSAPrivateKey) converter.getPrivateKey(keypair.getPrivateKeyInfo());
} else if (object instanceof PrivateKeyInfo) {
// Handle PKCS#8 format
return (RSAPrivateKey) converter.getPrivateKey((PrivateKeyInfo) object);
} else {
throw new IllegalArgumentException(
"Unsupported key format: "
+ (object != null ? object.getClass().getName() : "null"));
}
}
}
}

View File

@@ -12,6 +12,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;
@@ -20,11 +21,11 @@ import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.utils.RequestUriUtils;
@AllArgsConstructor
@Slf4j
public class CustomSaml2AuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler {
private LoginAttemptService loginAttemptService;
private ApplicationProperties applicationProperties;
private UserService userService;
@@ -34,10 +35,12 @@ public class CustomSaml2AuthenticationSuccessHandler
throws ServletException, IOException {
Object principal = authentication.getPrincipal();
log.debug("Starting SAML2 authentication success handling");
if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
String username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
// Get the saved request
log.debug("Authenticated principal found for user: {}", username);
HttpSession session = request.getSession(false);
String contextPath = request.getContextPath();
SavedRequest savedRequest =
@@ -45,46 +48,77 @@ public class CustomSaml2AuthenticationSuccessHandler
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
: null;
log.debug(
"Session exists: {}, Saved request exists: {}",
session != null,
savedRequest != null);
if (savedRequest != null
&& !RequestUriUtils.isStaticResource(
contextPath, savedRequest.getRedirectUrl())) {
// Redirect to the original destination
log.debug(
"Valid saved request found, redirecting to original destination: {}",
savedRequest.getRedirectUrl());
super.onAuthenticationSuccess(request, response, authentication);
} else {
SAML2 saml2 = applicationProperties.getSecurity().getSaml2();
log.debug(
"Processing SAML2 authentication with autoCreateUser: {}",
saml2.getAutoCreateUser());
if (loginAttemptService.isBlocked(username)) {
log.debug("User {} is blocked due to too many login attempts", 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)
&& saml2.getAutoCreateUser()) {
boolean userExists = userService.usernameExistsIgnoreCase(username);
boolean hasPassword = userExists && userService.hasPassword(username);
boolean isSSOUser =
userExists
&& userService.isAuthenticationTypeByUsername(
username, AuthenticationType.SSO);
log.debug(
"User status - Exists: {}, Has password: {}, Is SSO user: {}",
userExists,
hasPassword,
isSSOUser);
if (userExists && hasPassword && !isSSOUser && saml2.getAutoCreateUser()) {
log.debug(
"User {} exists with password but is not SSO user, redirecting to logout",
username);
response.sendRedirect(
contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
return;
}
try {
if (saml2.getBlockRegistration()
&& !userService.usernameExistsIgnoreCase(username)) {
if (saml2.getBlockRegistration() && !userExists) {
log.debug("Registration blocked for new user: {}", username);
response.sendRedirect(
contextPath + "/login?erroroauth=oauth2_admin_blocked_user");
return;
}
userService.processOAuth2PostLogin(username, saml2.getAutoCreateUser());
log.debug("Processing SSO post-login for user: {}", username);
userService.processSSOPostLogin(username, saml2.getAutoCreateUser());
log.debug("Successfully processed authentication for user: {}", username);
response.sendRedirect(contextPath + "/");
return;
} catch (IllegalArgumentException e) {
log.debug(
"Invalid username detected for user: {}, redirecting to logout",
username);
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
return;
}
}
} else {
log.debug("Non-SAML2 principal detected, delegating to parent handler");
super.onAuthenticationSuccess(request, response, authentication);
}
}

View File

@@ -3,8 +3,6 @@ package stirling.software.SPDF.config.security.saml2;
import java.util.*;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.schema.XSBoolean;
import org.opensaml.core.xml.schema.XSString;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeStatement;
@@ -30,15 +28,60 @@ public class CustomSaml2ResponseAuthenticationConverter
this.userService = userService;
}
private Map<String, List<Object>> extractAttributes(Assertion assertion) {
Map<String, List<Object>> attributes = new HashMap<>();
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
for (Attribute attribute : attributeStatement.getAttributes()) {
String attributeName = attribute.getName();
List<Object> values = new ArrayList<>();
for (XMLObject xmlObject : attribute.getAttributeValues()) {
// Get the text content directly
String value = xmlObject.getDOM().getTextContent();
if (value != null && !value.trim().isEmpty()) {
values.add(value);
}
}
if (!values.isEmpty()) {
// Store with both full URI and last part of the URI
attributes.put(attributeName, values);
String shortName = attributeName.substring(attributeName.lastIndexOf('/') + 1);
attributes.put(shortName, values);
}
}
}
return attributes;
}
@Override
public Saml2Authentication convert(ResponseToken responseToken) {
// Extract the assertion from the response
Assertion assertion = responseToken.getResponse().getAssertions().get(0);
Map<String, List<Object>> attributes = extractAttributes(assertion);
// Extract the NameID
String nameId = assertion.getSubject().getNameID().getValue();
// Debug log with actual values
log.debug("Extracted SAML Attributes: " + attributes);
Optional<User> userOpt = userService.findByUsernameIgnoreCase(nameId);
// Try to get username/identifier in order of preference
String userIdentifier = null;
if (hasAttribute(attributes, "username")) {
userIdentifier = getFirstAttributeValue(attributes, "username");
} else if (hasAttribute(attributes, "emailaddress")) {
userIdentifier = getFirstAttributeValue(attributes, "emailaddress");
} else if (hasAttribute(attributes, "name")) {
userIdentifier = getFirstAttributeValue(attributes, "name");
} else if (hasAttribute(attributes, "upn")) {
userIdentifier = getFirstAttributeValue(attributes, "upn");
} else if (hasAttribute(attributes, "uid")) {
userIdentifier = getFirstAttributeValue(attributes, "uid");
} else {
userIdentifier = assertion.getSubject().getNameID().getValue();
}
// Rest of your existing code...
Optional<User> userOpt = userService.findByUsernameIgnoreCase(userIdentifier);
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
if (userOpt.isPresent()) {
User user = userOpt.get();
@@ -48,39 +91,27 @@ public class CustomSaml2ResponseAuthenticationConverter
}
}
// Extract the SessionIndexes
List<String> sessionIndexes = new ArrayList<>();
for (AuthnStatement authnStatement : assertion.getAuthnStatements()) {
sessionIndexes.add(authnStatement.getSessionIndex());
}
// Extract the Attributes
Map<String, List<Object>> attributes = extractAttributes(assertion);
// Create the custom principal
CustomSaml2AuthenticatedPrincipal principal =
new CustomSaml2AuthenticatedPrincipal(nameId, attributes, nameId, sessionIndexes);
new CustomSaml2AuthenticatedPrincipal(
userIdentifier, attributes, userIdentifier, sessionIndexes);
// Create the Saml2Authentication
return new Saml2Authentication(
principal,
responseToken.getToken().getSaml2Response(),
Collections.singletonList(simpleGrantedAuthority));
}
private Map<String, List<Object>> extractAttributes(Assertion assertion) {
Map<String, List<Object>> attributes = new HashMap<>();
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
for (Attribute attribute : attributeStatement.getAttributes()) {
String attributeName = attribute.getName();
List<Object> values = new ArrayList<>();
for (XMLObject xmlObject : attribute.getAttributeValues()) {
log.info("BOOL: " + ((XSBoolean) xmlObject).getValue());
values.add(((XSString) xmlObject).getValue());
}
attributes.put(attributeName, values);
}
}
return attributes;
private boolean hasAttribute(Map<String, List<Object>> attributes, String name) {
return attributes.containsKey(name) && !attributes.get(name).isEmpty();
}
private String getFirstAttributeValue(Map<String, List<Object>> attributes, String name) {
List<Object> values = attributes.get(name);
return values != null && !values.isEmpty() ? values.get(0).toString() : null;
}
}

View File

@@ -244,8 +244,8 @@ public class UserController {
return new RedirectView("/addUsers?messageType=invalidRole", true);
}
if (authType.equalsIgnoreCase(AuthenticationType.OAUTH2.toString())) {
userService.saveUser(username, AuthenticationType.OAUTH2, role);
if (authType.equalsIgnoreCase(AuthenticationType.SSO.toString())) {
userService.saveUser(username, AuthenticationType.SSO, role);
} else {
if (password.isBlank()) {
return new RedirectView("/addUsers?messageType=invalidPassword", true);

View File

@@ -1,12 +1,13 @@
package stirling.software.SPDF.controller.api.converters;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
@@ -37,59 +38,90 @@ public class ConvertPDFToPDFA {
@Operation(
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")
"This endpoint converts a PDF file to a PDF/A file using LibreOffice. PDF/A is a format designed for long-term archiving of digital documents. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> pdfToPdfA(@ModelAttribute PdfToPdfARequest request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
String outputFormat = request.getOutputFormat();
// Convert MultipartFile to byte[]
byte[] pdfBytes = inputFile.getBytes();
// Save the uploaded file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf");
try (OutputStream outputStream = new FileOutputStream(tempInputFile.toFile())) {
outputStream.write(pdfBytes);
// Validate input file type
if (!"application/pdf".equals(inputFile.getContentType())) {
logger.error("Invalid input file type: {}", inputFile.getContentType());
throw new IllegalArgumentException("Input file must be a PDF");
}
// Prepare the output file path
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
// Prepare the ghostscript command
List<String> command = new ArrayList<>();
command.add("gs");
command.add("-dPDFA=" + ("pdfa".equals(outputFormat) ? "2" : "1"));
command.add("-dNOPAUSE");
command.add("-dBATCH");
command.add("-sColorConversionStrategy=sRGB");
command.add("-sDEVICE=pdfwrite");
command.add("-dPDFACompatibilityPolicy=2");
command.add("-o");
command.add(tempOutputFile.toString());
command.add(tempInputFile.toString());
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT)
.runCommandWithOutputHandling(command);
if (returnCode.getRc() != 0) {
logger.info(
outputFormat + " conversion failed with return code: " + returnCode.getRc());
// Get the original filename without extension
String originalFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
if (originalFileName == null || originalFileName.trim().isEmpty()) {
originalFileName = "output.pdf";
}
String baseFileName =
originalFileName.contains(".")
? originalFileName.substring(0, originalFileName.lastIndexOf('.'))
: originalFileName;
Path tempInputFile = null;
Path tempOutputDir = null;
byte[] fileBytes;
try {
byte[] pdfBytesOutput = Files.readAllBytes(tempOutputFile);
// Return the optimized PDF as a response
String outputFilename =
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "")
+ "_PDFA.pdf";
// Save uploaded file to temp location
tempInputFile = Files.createTempFile("input_", ".pdf");
inputFile.transferTo(tempInputFile);
// Create temp output directory
tempOutputDir = Files.createTempDirectory("output_");
// Determine PDF/A filter based on requested format
String pdfFilter =
"pdfa".equals(outputFormat)
? "writer_pdf_Export:{'SelectPdfVersion':{'Value':'2'}}:writer_pdf_Export"
: "writer_pdf_Export:{'SelectPdfVersion':{'Value':'1'}}:writer_pdf_Export";
// Prepare LibreOffice command
List<String> command =
new ArrayList<>(
Arrays.asList(
"soffice",
"--headless",
"--nologo",
"--convert-to",
"pdf:" + pdfFilter,
"--outdir",
tempOutputDir.toString(),
tempInputFile.toString()));
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE)
.runCommandWithOutputHandling(command);
if (returnCode.getRc() != 0) {
logger.error("PDF/A conversion failed with return code: {}", returnCode.getRc());
throw new RuntimeException("PDF/A conversion failed");
}
// Get the output file
File[] outputFiles = tempOutputDir.toFile().listFiles();
if (outputFiles == null || outputFiles.length != 1) {
throw new RuntimeException(
"Expected exactly one output file but found "
+ (outputFiles == null ? "none" : outputFiles.length));
}
fileBytes = FileUtils.readFileToByteArray(outputFiles[0]);
String outputFilename = baseFileName + "_PDFA.pdf";
return WebResponseUtils.bytesToWebResponse(
pdfBytesOutput, outputFilename, MediaType.APPLICATION_PDF);
fileBytes, outputFilename, MediaType.APPLICATION_PDF);
} finally {
// Clean up the temporary files
Files.deleteIfExists(tempInputFile);
Files.deleteIfExists(tempOutputFile);
// Clean up temporary files
if (tempInputFile != null) {
Files.deleteIfExists(tempInputFile);
}
if (tempOutputDir != null) {
FileUtils.deleteDirectory(tempOutputDir.toFile());
}
}
}
}

View File

@@ -1,12 +1,12 @@
package stirling.software.SPDF.controller.api.converters;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.QuoteMode;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ContentDisposition;
@@ -18,21 +18,23 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.opencsv.CSVWriter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.controller.api.CropController;
import stirling.software.SPDF.controller.api.strippers.PDFTableStripper;
import stirling.software.SPDF.model.api.extract.PDFFilePage;
import stirling.software.SPDF.pdf.FlexibleCSVWriter;
import technology.tabula.ObjectExtractor;
import technology.tabula.Page;
import technology.tabula.Table;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;
import technology.tabula.writers.Writer;
@RestController
@RequestMapping("/api/v1/convert")
@Tag(name = "Convert", description = "Convert APIs")
public class ExtractCSVController {
private static final Logger logger = LoggerFactory.getLogger(CropController.class);
private static final Logger logger = LoggerFactory.getLogger(ExtractCSVController.class);
@PostMapping(value = "/pdf/csv", consumes = "multipart/form-data")
@Operation(
@@ -40,57 +42,16 @@ public class ExtractCSVController {
description =
"This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO")
public ResponseEntity<String> PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception {
ArrayList<String> tableData = new ArrayList<>();
int columnsCount = 0;
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
final double res = 72; // PDF units are at 72 DPI
PDFTableStripper stripper = new PDFTableStripper();
PDPage pdPage = document.getPage(form.getPageId() - 1);
stripper.extractTable(pdPage);
columnsCount = stripper.getColumns();
for (int c = 0; c < columnsCount; ++c) {
for (int r = 0; r < stripper.getRows(); ++r) {
tableData.add(stripper.getText(r, c));
}
}
}
ArrayList<String> notEmptyColumns = new ArrayList<>();
for (String item : tableData) {
if (!item.trim().isEmpty()) {
notEmptyColumns.add(item);
} else {
columnsCount--;
}
}
List<String> fullTable =
notEmptyColumns.stream()
.map(
(entity) ->
entity.replace('\n', ' ')
.replace('\r', ' ')
.trim()
.replaceAll("\\s{2,}", "|"))
.toList();
int rowsCount = fullTable.get(0).split("\\|").length;
ArrayList<String> headersList = getTableHeaders(columnsCount, fullTable);
ArrayList<String> recordList = getRecordsList(rowsCount, fullTable);
if (headersList.size() == 0 && recordList.size() == 0) {
throw new Exception("No table detected, no headers or records found");
}
StringWriter writer = new StringWriter();
try (CSVWriter csvWriter = new CSVWriter(writer)) {
csvWriter.writeNext(headersList.toArray(new String[0]));
for (String record : recordList) {
csvWriter.writeNext(record.split("\\|"));
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
CSVFormat format =
CSVFormat.EXCEL.builder().setEscape('"').setQuoteMode(QuoteMode.ALL).build();
Writer csvWriter = new FlexibleCSVWriter(format);
SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm();
try (ObjectExtractor extractor = new ObjectExtractor(document)) {
Page page = extractor.extract(form.getPageId());
List<Table> tables = sea.extract(page);
csvWriter.write(writer, tables);
}
}
@@ -107,33 +68,4 @@ public class ExtractCSVController {
return ResponseEntity.ok().headers(headers).body(writer.toString());
}
private ArrayList<String> getRecordsList(int rowsCounts, List<String> items) {
ArrayList<String> recordsList = new ArrayList<>();
for (int b = 1; b < rowsCounts; b++) {
StringBuilder strbldr = new StringBuilder();
for (int i = 0; i < items.size(); i++) {
String[] parts = items.get(i).split("\\|");
strbldr.append(parts[b]);
if (i != items.size() - 1) {
strbldr.append("|");
}
}
recordsList.add(strbldr.toString());
}
return recordsList;
}
private ArrayList<String> getTableHeaders(int columnsCount, List<String> items) {
ArrayList<String> resultList = new ArrayList<>();
for (int i = 0; i < columnsCount; i++) {
String[] parts = items.get(i).split("\\|");
resultList.add(parts[0]);
}
return resultList;
}
}

View File

@@ -10,7 +10,6 @@ import java.util.List;
import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -53,6 +52,54 @@ public class CompressController {
this.pdfDocumentFactory = pdfDocumentFactory;
}
private void compressImagesInPDF(Path pdfFile, double initialScaleFactor) throws Exception {
byte[] fileBytes = Files.readAllBytes(pdfFile);
try (PDDocument doc = Loader.loadPDF(fileBytes)) {
double scaleFactor = initialScaleFactor;
for (PDPage page : doc.getPages()) {
PDResources res = page.getResources();
if (res != null && res.getXObjectNames() != null) {
for (COSName name : res.getXObjectNames()) {
PDXObject xobj = res.getXObject(name);
if (xobj instanceof PDImageXObject) {
PDImageXObject image = (PDImageXObject) xobj;
BufferedImage bufferedImage = image.getImage();
int newWidth = (int) (bufferedImage.getWidth() * scaleFactor);
int newHeight = (int) (bufferedImage.getHeight() * scaleFactor);
if (newWidth == 0 || newHeight == 0) {
continue;
}
Image scaledImage =
bufferedImage.getScaledInstance(
newWidth, newHeight, Image.SCALE_SMOOTH);
BufferedImage scaledBufferedImage =
new BufferedImage(
newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
ByteArrayOutputStream compressedImageStream =
new ByteArrayOutputStream();
ImageIO.write(scaledBufferedImage, "jpeg", compressedImageStream);
byte[] imageBytes = compressedImageStream.toByteArray();
compressedImageStream.close();
PDImageXObject compressedImage =
PDImageXObject.createFromByteArray(
doc, imageBytes, image.getCOSObject().toString());
res.put(name, compressedImage);
}
}
}
}
doc.save(pdfFile.toString());
}
}
@PostMapping(consumes = "multipart/form-data", value = "/compress-pdf")
@Operation(
summary = "Optimize PDF file",
@@ -75,209 +122,92 @@ public class CompressController {
autoMode = true;
}
// Save the uploaded file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf");
inputFile.transferTo(tempInputFile.toFile());
long inputFileSize = Files.size(tempInputFile);
// Prepare the output file path
Path tempOutputFile = null;
byte[] pdfBytes;
try {
tempOutputFile = Files.createTempFile("output_", ".pdf");
// Determine initial optimization level based on expected size reduction, only if in
// autoMode
if (autoMode) {
double sizeReductionRatio = expectedOutputSize / (double) inputFileSize;
if (sizeReductionRatio > 0.7) {
optimizeLevel = 1;
} else if (sizeReductionRatio > 0.5) {
optimizeLevel = 2;
} else if (sizeReductionRatio > 0.35) {
optimizeLevel = 3;
} else {
optimizeLevel = 3;
}
optimizeLevel = determineOptimizeLevel(sizeReductionRatio);
}
boolean sizeMet = false;
while (!sizeMet && optimizeLevel <= 4) {
// Prepare the Ghostscript command
List<String> command = new ArrayList<>();
command.add("gs");
command.add("-sDEVICE=pdfwrite");
command.add("-dCompatibilityLevel=1.5");
while (!sizeMet && optimizeLevel <= 9) {
switch (optimizeLevel) {
case 1:
command.add("-dPDFSETTINGS=/prepress");
break;
case 2:
command.add("-dPDFSETTINGS=/printer");
break;
case 3:
command.add("-dPDFSETTINGS=/ebook");
break;
case 4:
command.add("-dPDFSETTINGS=/screen");
break;
default:
command.add("-dPDFSETTINGS=/default");
// Apply additional image compression for levels 6-9
if (optimizeLevel >= 6) {
// Calculate scale factor based on optimization level
double scaleFactor =
switch (optimizeLevel) {
case 6 -> 0.9; // 90% of original size
case 7 -> 0.8; // 80% of original size
case 8 -> 0.65; // 70% of original size
case 9 -> 0.5; // 60% of original size
default -> 1.0;
};
compressImagesInPDF(tempInputFile, scaleFactor);
}
command.add("-dNOPAUSE");
command.add("-dQUIET");
command.add("-dBATCH");
command.add("-sOutputFile=" + tempOutputFile.toString());
// Run QPDF optimization
List<String> command = new ArrayList<>();
command.add("qpdf");
if (request.getNormalize()) {
command.add("--normalize-content=y");
}
if (request.getLinearize()) {
command.add("--linearize");
}
command.add("--optimize-images");
command.add("--recompress-flate");
command.add("--compression-level=" + optimizeLevel);
command.add("--compress-streams=y");
command.add("--object-streams=generate");
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT)
.runCommandWithOutputHandling(command);
ProcessExecutorResult returnCode = null;
try {
returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
.runCommandWithOutputHandling(command);
} catch (Exception e) {
if (returnCode != null && returnCode.getRc() != 3) {
throw e;
}
}
// Check if file size is within expected size or not auto mode so instantly finish
// Check if file size is within expected size or not auto mode
long outputFileSize = Files.size(tempOutputFile);
if (outputFileSize <= expectedOutputSize || !autoMode) {
sizeMet = true;
} else {
// Increase optimization level for next iteration
optimizeLevel++;
if (autoMode && optimizeLevel > 4) {
logger.info("Skipping level 5 due to bad results in auto mode");
optimizeLevel =
incrementOptimizeLevel(
optimizeLevel, outputFileSize, expectedOutputSize);
if (autoMode && optimizeLevel > 9) {
logger.info("Maximum compression level reached in auto mode");
sizeMet = true;
} else {
logger.info(
"Increasing ghostscript optimisation level to " + optimizeLevel);
}
}
}
if (expectedOutputSize != null && autoMode) {
long outputFileSize = Files.size(tempOutputFile);
byte[] fileBytes = Files.readAllBytes(tempOutputFile);
if (outputFileSize > expectedOutputSize) {
try (PDDocument doc = Loader.loadPDF(fileBytes)) {
long previousFileSize = 0;
double scaleFactorConst = 0.9f;
double scaleFactor = 0.9f;
while (true) {
for (PDPage page : doc.getPages()) {
PDResources res = page.getResources();
if (res != null && res.getXObjectNames() != null) {
for (COSName name : res.getXObjectNames()) {
PDXObject xobj = res.getXObject(name);
if (xobj != null && xobj instanceof PDImageXObject) {
PDImageXObject image = (PDImageXObject) xobj;
// Get the image in BufferedImage format
BufferedImage bufferedImage = image.getImage();
// Calculate the new dimensions
int newWidth =
(int)
(bufferedImage.getWidth()
* scaleFactorConst);
int newHeight =
(int)
(bufferedImage.getHeight()
* scaleFactorConst);
// If the new dimensions are zero, skip this iteration
if (newWidth == 0 || newHeight == 0) {
continue;
}
// Otherwise, proceed with the scaling
Image scaledImage =
bufferedImage.getScaledInstance(
newWidth,
newHeight,
Image.SCALE_SMOOTH);
// Convert the scaled image back to a BufferedImage
BufferedImage scaledBufferedImage =
new BufferedImage(
newWidth,
newHeight,
BufferedImage.TYPE_INT_RGB);
scaledBufferedImage
.getGraphics()
.drawImage(scaledImage, 0, 0, null);
// Compress the scaled image
ByteArrayOutputStream compressedImageStream =
new ByteArrayOutputStream();
ImageIO.write(
scaledBufferedImage,
"jpeg",
compressedImageStream);
byte[] imageBytes = compressedImageStream.toByteArray();
compressedImageStream.close();
PDImageXObject compressedImage =
PDImageXObject.createFromByteArray(
doc,
imageBytes,
image.getCOSObject().toString());
// Replace the image in the resources with the
// compressed
// version
res.put(name, compressedImage);
}
}
}
}
// save the document to tempOutputFile again
doc.save(tempOutputFile.toString());
long currentSize = Files.size(tempOutputFile);
// Check if the overall PDF size is still larger than expectedOutputSize
if (currentSize > expectedOutputSize) {
// Log the current file size and scaleFactor
logger.info(
"Current file size: "
+ FileUtils.byteCountToDisplaySize(currentSize));
logger.info("Current scale factor: " + scaleFactor);
// The file is still too large, reduce scaleFactor and try again
scaleFactor *= 0.9f; // reduce scaleFactor by 10%
// Avoid scaleFactor being too small, causing the image to shrink to
// 0
if (scaleFactor < 0.2f || previousFileSize == currentSize) {
throw new RuntimeException(
"Could not reach the desired size without excessively degrading image quality, lowest size recommended is "
+ FileUtils.byteCountToDisplaySize(currentSize)
+ ", "
+ currentSize
+ " bytes");
}
previousFileSize = currentSize;
} else {
// The file is small enough, break the loop
break;
}
}
}
}
}
// Read the optimized PDF file
pdfBytes = Files.readAllBytes(tempOutputFile);
Path finalFile = tempOutputFile;
// Check if optimized file is larger than the original
if (pdfBytes.length > inputFileSize) {
// Log the occurrence
logger.warn(
"Optimized file is larger than the original. Returning the original file instead.");
// Read the original file again
finalFile = tempInputFile;
}
// Return the optimized PDF as a response
String outputFilename =
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "")
@@ -286,10 +216,31 @@ public class CompressController {
pdfDocumentFactory.load(finalFile.toFile()), outputFilename);
} finally {
// Clean up the temporary files
// deleted by multipart file handler deu to transferTo?
// Files.deleteIfExists(tempInputFile);
Files.deleteIfExists(tempOutputFile);
}
}
private int determineOptimizeLevel(double sizeReductionRatio) {
if (sizeReductionRatio > 0.9) return 1;
if (sizeReductionRatio > 0.8) return 2;
if (sizeReductionRatio > 0.7) return 3;
if (sizeReductionRatio > 0.6) return 4;
if (sizeReductionRatio > 0.5) return 5;
if (sizeReductionRatio > 0.4) return 6;
if (sizeReductionRatio > 0.3) return 7;
if (sizeReductionRatio > 0.2) return 8;
return 9;
}
private int incrementOptimizeLevel(int currentLevel, long currentSize, long targetSize) {
double currentRatio = currentSize / (double) targetSize;
logger.info("Current compression ratio: {}", String.format("%.2f", currentRatio));
if (currentRatio > 2.0) {
return Math.min(9, currentLevel + 3);
} else if (currentRatio > 1.5) {
return Math.min(9, currentLevel + 2);
}
return Math.min(9, currentLevel + 1);
}
}

View File

@@ -58,7 +58,7 @@ public class FakeScanControllerWIP {
@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.")
"This endpoint repairs a given PDF file by running qpdf command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response.")
public ResponseEntity<byte[]> fakeScan(@ModelAttribute PDFFile request) throws IOException {
MultipartFile inputFile = request.getFileInput();

View File

@@ -14,6 +14,8 @@ import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -26,6 +28,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.misc.MetadataRequest;
import stirling.software.SPDF.utils.WebResponseUtils;
import stirling.software.SPDF.utils.propertyeditor.StringToMapPropertyEditor;
@RestController
@RequestMapping("/api/v1/misc")
@@ -44,6 +47,11 @@ public class MetadataController {
return entry;
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Map.class, "allRequestParams", new StringToMapPropertyEditor());
}
@PostMapping(consumes = "multipart/form-data", value = "/update-metadata")
@Operation(
summary = "Update metadata of a PDF file",

View File

@@ -1,19 +1,29 @@
package stirling.software.SPDF.controller.api.misc;
import java.io.ByteArrayInputStream;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.imageio.ImageIO;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -23,24 +33,31 @@ 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.BoundedLineReader;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs")
@Slf4j
public class OCRController {
@Autowired ApplicationProperties applicationProperties;
@Autowired private ApplicationProperties applicationProperties;
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public OCRController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
/** Gets the list of available Tesseract languages from the tessdata directory */
public List<String> getAvailableTesseractLanguages() {
String tessdataDir = applicationProperties.getSystem().getTessdataDir();
File[] files = new File(tessdataDir).listFiles();
@@ -54,196 +71,163 @@ public class OCRController {
.collect(Collectors.toList());
}
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public OCRController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
@Operation(
summary = "Process a PDF file with OCR",
description =
"This endpoint processes a PDF file using OCR (Optical Character Recognition). Users can specify languages, sidecar, deskew, clean, cleanFinal, ocrType, ocrRenderType, and removeImagesAfter options. Input:PDF Output:PDF Type:SI-Conditional")
public ResponseEntity<byte[]> processPdfWithOCR(
@ModelAttribute ProcessPdfWithOcrRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
List<String> selectedLanguages = request.getLanguages();
Boolean sidecar = request.isSidecar();
Boolean deskew = request.isDeskew();
Boolean clean = request.isClean();
Boolean cleanFinal = request.isCleanFinal();
List<String> languages = request.getLanguages();
String ocrType = request.getOcrType();
String ocrRenderType = request.getOcrRenderType();
Boolean removeImagesAfter = request.isRemoveImagesAfter();
// --output-type pdfa
if (selectedLanguages == null || selectedLanguages.isEmpty()) {
throw new IOException("Please select at least one language.");
}
if (!"hocr".equals(ocrRenderType) && !"sandwich".equals(ocrRenderType)) {
throw new IOException("ocrRenderType wrong");
}
Path tempDir = Files.createTempDirectory("ocr_process");
Path tempInputFile = tempDir.resolve("input.pdf");
Path tempOutputDir = tempDir.resolve("output");
Path tempImagesDir = tempDir.resolve("images");
Path finalOutputFile = tempDir.resolve("final_output.pdf");
// Get available Tesseract languages
List<String> availableLanguages = getAvailableTesseractLanguages();
// Validate selected languages
selectedLanguages =
selectedLanguages.stream().filter(availableLanguages::contains).toList();
if (selectedLanguages.isEmpty()) {
throw new IOException("None of the selected languages are valid.");
}
// Save the uploaded file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf");
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
Path sidecarTextPath = null;
Files.createDirectories(tempOutputDir);
Files.createDirectories(tempImagesDir);
try {
// Save input file
inputFile.transferTo(tempInputFile.toFile());
PDFMergerUtility merger = new PDFMergerUtility();
merger.setDestinationFileName(finalOutputFile.toString());
// Run OCR Command
String languageOption = String.join("+", selectedLanguages);
try (PDDocument document = pdfDocumentFactory.load(tempInputFile.toFile())) {
PDFRenderer pdfRenderer = new PDFRenderer(document);
int pageCount = document.getNumberOfPages();
List<String> command =
new ArrayList<>(
Arrays.asList(
"ocrmypdf",
"--verbose",
"2",
"--output-type",
"pdf",
"--pdf-renderer",
ocrRenderType));
for (int pageNum = 0; pageNum < pageCount; pageNum++) {
PDPage page = document.getPage(pageNum);
boolean hasText = false;
if (sidecar != null && sidecar) {
sidecarTextPath = Files.createTempFile("sidecar", ".txt");
command.add("--sidecar");
command.add(sidecarTextPath.toString());
}
// Check for existing text
try (PDDocument tempDoc = new PDDocument()) {
tempDoc.addPage(page);
PDFTextStripper stripper = new PDFTextStripper();
hasText = !stripper.getText(tempDoc).trim().isEmpty();
}
if (deskew != null && deskew) {
command.add("--deskew");
}
if (clean != null && clean) {
command.add("--clean");
}
if (cleanFinal != null && cleanFinal) {
command.add("--clean-final");
}
if (ocrType != null && !"".equals(ocrType)) {
if ("skip-text".equals(ocrType)) {
command.add("--skip-text");
} else if ("force-ocr".equals(ocrType)) {
command.add("--force-ocr");
} else if ("Normal".equals(ocrType)) {
boolean shouldOcr =
switch (ocrType) {
case "skip-text" -> !hasText;
case "force-ocr" -> true;
default -> true;
};
Path pageOutputPath =
tempOutputDir.resolve(String.format("page_%d.pdf", pageNum));
if (shouldOcr) {
// Convert page to image
BufferedImage image = pdfRenderer.renderImageWithDPI(pageNum, 300);
Path imagePath =
tempImagesDir.resolve(String.format("page_%d.png", pageNum));
ImageIO.write(image, "png", imagePath.toFile());
// Build OCR command
List<String> command = new ArrayList<>();
command.add("tesseract");
command.add(imagePath.toString());
command.add(
tempOutputDir
.resolve(String.format("page_%d", pageNum))
.toString());
command.add("-l");
command.add(String.join("+", languages));
command.add("pdf"); // Always output PDF
ProcessBuilder pb = new ProcessBuilder(command);
Process process = pb.start();
// Capture any error output
try (BufferedReader reader =
new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
log.debug("Tesseract: {}", line);
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException(
"Tesseract failed with exit code: " + exitCode);
}
// Add OCR'd PDF to merger
merger.addSource(pageOutputPath.toFile());
} else {
// Save original page without OCR
try (PDDocument pageDoc = new PDDocument()) {
pageDoc.addPage(page);
pageDoc.save(pageOutputPath.toFile());
merger.addSource(pageOutputPath.toFile());
}
}
}
}
command.addAll(
Arrays.asList(
"--language",
languageOption,
tempInputFile.toString(),
tempOutputFile.toString()));
// Merge all pages into final PDF
merger.mergeDocuments(null);
// Run CLI command
ProcessExecutorResult result =
ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF)
.runCommandWithOutputHandling(command);
if (result.getRc() != 0
&& result.getMessages().contains("multiprocessing/synchronize.py")
&& result.getMessages()
.contains("OSError: [Errno 38] Function not implemented")) {
command.add("--jobs");
command.add("1");
result =
ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF)
.runCommandWithOutputHandling(command);
}
// Remove images from the OCR processed PDF if the flag is set to true
if (removeImagesAfter != null && removeImagesAfter) {
Path tempPdfWithoutImages = Files.createTempFile("output_", "_no_images.pdf");
List<String> gsCommand =
Arrays.asList(
"gs",
"-sDEVICE=pdfwrite",
"-dFILTERIMAGE",
"-o",
tempPdfWithoutImages.toString(),
tempOutputFile.toString());
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT)
.runCommandWithOutputHandling(gsCommand);
tempOutputFile = tempPdfWithoutImages;
}
// Read the OCR processed PDF file
byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile());
// Return the OCR processed PDF as a response
// Read the final PDF file
byte[] pdfContent = Files.readAllBytes(finalOutputFile);
String outputFilename =
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "")
+ "_OCR.pdf";
if (sidecar != null && sidecar) {
// Create a zip file containing both the PDF and the text file
String outputZipFilename =
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "")
+ "_OCR.zip";
Path tempZipFile = Files.createTempFile("output_", ".zip");
return ResponseEntity.ok()
.header(
"Content-Disposition",
"attachment; filename=\"" + outputFilename + "\"")
.contentType(MediaType.APPLICATION_PDF)
.body(pdfContent);
try (ZipOutputStream zipOut =
new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
// Add PDF file to the zip
ZipEntry pdfEntry = new ZipEntry(outputFilename);
zipOut.putNextEntry(pdfEntry);
try (ByteArrayInputStream pdfInputStream = new ByteArrayInputStream(pdfBytes)) {
byte[] buffer = new byte[1024];
int length;
while ((length = pdfInputStream.read(buffer)) != -1) {
zipOut.write(buffer, 0, length);
}
}
zipOut.closeEntry();
// Add text file to the zip
ZipEntry txtEntry = new ZipEntry(outputFilename.replace(".pdf", ".txt"));
zipOut.putNextEntry(txtEntry);
Files.copy(sidecarTextPath, zipOut);
zipOut.closeEntry();
}
byte[] zipBytes = Files.readAllBytes(tempZipFile);
// Clean up the temporary zip file
Files.deleteIfExists(tempZipFile);
Files.deleteIfExists(tempOutputFile);
Files.deleteIfExists(sidecarTextPath);
// Return the zip file containing both the PDF and the text file
return WebResponseUtils.bytesToWebResponse(
zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
} else {
// Return the OCR processed PDF as a response
Files.deleteIfExists(tempOutputFile);
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
} finally {
// Clean up the temporary files
Files.deleteIfExists(tempOutputFile);
// Comment out as transferTo makes multipart handle cleanup
// Files.deleteIfExists(tempInputFile);
if (sidecarTextPath != null) {
Files.deleteIfExists(sidecarTextPath);
// Clean up temporary files
deleteDirectory(tempDir);
}
}
private void addFileToZip(File file, String filename, ZipOutputStream zipOut)
throws IOException {
if (!file.exists()) {
log.warn("File {} does not exist, skipping", file);
return;
}
try (FileInputStream fis = new FileInputStream(file)) {
ZipEntry zipEntry = new ZipEntry(filename);
zipOut.putNextEntry(zipEntry);
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) >= 0) {
zipOut.write(buffer, 0, length);
}
zipOut.closeEntry();
}
}
private void deleteDirectory(Path directory) {
try {
Files.walk(directory)
.sorted(Comparator.reverseOrder())
.forEach(
path -> {
try {
Files.delete(path);
} catch (IOException e) {
log.error("Error deleting {}: {}", path, e.getMessage());
}
});
} catch (IOException e) {
log.error("Error walking directory {}: {}", directory, e.getMessage());
}
}
}

View File

@@ -44,30 +44,29 @@ public class RepairController {
@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. Input:PDF Output:PDF Type:SISO")
"This endpoint repairs a given PDF file by running qpdf command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
// Save the uploaded file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf");
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
byte[] pdfBytes = null;
inputFile.transferTo(tempInputFile.toFile());
try {
List<String> command = new ArrayList<>();
command.add("gs");
command.add("-o");
command.add(tempOutputFile.toString());
command.add("-sDEVICE=pdfwrite");
command.add("qpdf");
command.add("--replace-input"); // Automatically fixes problems it can
command.add("--qdf"); // Linearizes and normalizes PDF structure
command.add("--object-streams=disable"); // Can help with some corruptions
command.add(tempInputFile.toString());
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT)
ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
.runCommandWithOutputHandling(command);
// Read the optimized PDF file
pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile());
pdfBytes = pdfDocumentFactory.loadToBytes(tempInputFile.toFile());
// Return the optimized PDF as a response
String outputFilename =
@@ -78,7 +77,6 @@ public class RepairController {
} finally {
// Clean up the temporary files
Files.deleteIfExists(tempInputFile);
Files.deleteIfExists(tempOutputFile);
}
}
}

View File

@@ -147,7 +147,7 @@ public class StampController {
return WebResponseUtils.pdfDocToWebResponse(
document,
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "")
.replaceFirst("[.][^.]+$", "")
+ "_stamped.pdf");
}
@@ -191,7 +191,7 @@ public class StampController {
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
File tempFile = Files.createTempFile("NotoSansFont", fileExtension).toFile();
try (InputStream is = classPathResource.getInputStream();
FileOutputStream os = new FileOutputStream(tempFile)) {
FileOutputStream os = new FileOutputStream(tempFile)) {
IOUtils.copy(is, os);
font = PDType0Font.load(document, tempFile);
} finally {
@@ -229,10 +229,22 @@ public class StampController {
calculatePositionY(
pageSize, position, calculateTextCapHeight(font, fontSize), margin);
}
// Split the stampText into multiple lines
String[] lines = stampText.split("\\\\n");
// Calculate dynamic line height based on font ascent and descent
float ascent = font.getFontDescriptor().getAscent();
float descent = font.getFontDescriptor().getDescent();
float lineHeight = ((ascent - descent) / 1000) * fontSize;
contentStream.beginText();
contentStream.setTextMatrix(Matrix.getRotateInstance(Math.toRadians(rotation), x, y));
contentStream.showText(stampText);
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
// Set the text matrix for each line with rotation
contentStream.setTextMatrix(
Matrix.getRotateInstance(Math.toRadians(rotation), x, y - (i * lineHeight)));
contentStream.showText(line);
}
contentStream.endText();
}
@@ -327,4 +339,4 @@ public class StampController {
private float calculateTextCapHeight(PDFont font, float fontSize) {
return font.getFontDescriptor().getCapHeight() / 1000 * fontSize;
}
}
}

View File

@@ -1,327 +0,0 @@
package stirling.software.SPDF.controller.api.strippers;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.fontbox.util.BoundingBox;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType3Font;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.PDFTextStripperByArea;
import org.apache.pdfbox.text.TextPosition;
/**
* Class to extract tabular data from a PDF. Works by making a first pass of the page to group all
* nearby text items together, and then inferring a 2D grid from these regions. Each table cell is
* then extracted using a PDFTextStripperByArea object.
*
* <p>Works best when headers are included in the detected region, to ensure representative text in
* every column.
*
* <p>Based upon DrawPrintTextLocations PDFBox example
* (https://svn.apache.org/viewvc/pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/util/DrawPrintTextLocations.java)
*
* @author Beldaz
*/
public class PDFTableStripper extends PDFTextStripper {
/**
* This will print the documents data, for each table cell.
*
* @param args The command line arguments.
* @throws IOException If there is an error parsing the document.
*/
/*
* Used in methods derived from DrawPrintTextLocations
*/
private AffineTransform flipAT;
private AffineTransform rotateAT;
/** Regions updated by calls to writeString */
private Set<Rectangle2D> boxes;
// Border to allow when finding intersections
private double dx = 1.0; // This value works for me, feel free to tweak (or add setter)
private double dy = 0.000; // Rows of text tend to overlap, so need to extend
/** Region in which to find table (otherwise whole page) */
private Rectangle2D regionArea;
/** Number of rows in inferred table */
private int nRows = 0;
/** Number of columns in inferred table */
private int nCols = 0;
/** This is the object that does the text extraction */
private PDFTextStripperByArea regionStripper;
/**
* 1D intervals - used for calculateTableRegions()
*
* @author Beldaz
*/
public static class Interval {
double start;
double end;
public Interval(double start, double end) {
this.start = start;
this.end = end;
}
public void add(Interval col) {
if (col.start < start) start = col.start;
if (col.end > end) end = col.end;
}
public static void addTo(Interval x, LinkedList<Interval> columns) {
int p = 0;
Iterator<Interval> it = columns.iterator();
// Find where x should go
while (it.hasNext()) {
Interval col = it.next();
if (x.end >= col.start) {
if (x.start <= col.end) { // overlaps
x.add(col);
it.remove();
}
break;
}
++p;
}
while (it.hasNext()) {
Interval col = it.next();
if (x.start > col.end) break;
x.add(col);
it.remove();
}
columns.add(p, x);
}
}
/**
* Instantiate a new PDFTableStripper object.
*
* @throws IOException If there is an error loading the properties.
*/
public PDFTableStripper() throws IOException {
super.setShouldSeparateByBeads(false);
regionStripper = new PDFTextStripperByArea();
regionStripper.setSortByPosition(true);
}
/**
* Define the region to group text by.
*
* @param rect The rectangle area to retrieve the text from.
*/
public void setRegion(Rectangle2D rect) {
regionArea = rect;
}
public int getRows() {
return nRows;
}
public int getColumns() {
return nCols;
}
/**
* Get the text for the region, this should be called after extractTable().
*
* @return The text that was identified in that region.
*/
public String getText(int row, int col) {
return regionStripper.getTextForRegion("el" + col + "x" + row);
}
public void extractTable(PDPage pdPage) throws IOException {
setStartPage(getCurrentPageNo());
setEndPage(getCurrentPageNo());
boxes = new HashSet<Rectangle2D>();
// flip y-axis
flipAT = new AffineTransform();
flipAT.translate(0, pdPage.getBBox().getHeight());
flipAT.scale(1, -1);
// page may be rotated
rotateAT = new AffineTransform();
int rotation = pdPage.getRotation();
if (rotation != 0) {
PDRectangle mediaBox = pdPage.getMediaBox();
switch (rotation) {
case 90:
rotateAT.translate(mediaBox.getHeight(), 0);
break;
case 270:
rotateAT.translate(0, mediaBox.getWidth());
break;
case 180:
rotateAT.translate(mediaBox.getWidth(), mediaBox.getHeight());
break;
default:
break;
}
rotateAT.rotate(Math.toRadians(rotation));
}
// Trigger processing of the document so that writeString is called.
try (Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream())) {
super.output = dummy;
super.processPage(pdPage);
}
Rectangle2D[][] regions = calculateTableRegions();
// System.err.println("Drawing " + nCols + "x" + nRows + "="+ nRows*nCols + "
// regions");
for (int i = 0; i < nCols; ++i) {
for (int j = 0; j < nRows; ++j) {
final Rectangle2D region = regions[i][j];
regionStripper.addRegion("el" + i + "x" + j, region);
}
}
regionStripper.extractRegions(pdPage);
}
/**
* Infer a rectangular grid of regions from the boxes field.
*
* @return 2D array of table regions (as Rectangle2D objects). Note that some of these regions
* may have no content.
*/
private Rectangle2D[][] calculateTableRegions() {
// Build up a list of all table regions, based upon the populated
// regions of boxes field. Treats the horizontal and vertical extents
// of each box as distinct
LinkedList<Interval> columns = new LinkedList<Interval>();
LinkedList<Interval> rows = new LinkedList<Interval>();
for (Rectangle2D box : boxes) {
Interval x = new Interval(box.getMinX(), box.getMaxX());
Interval y = new Interval(box.getMinY(), box.getMaxY());
Interval.addTo(x, columns);
Interval.addTo(y, rows);
}
nRows = rows.size();
nCols = columns.size();
Rectangle2D[][] regions = new Rectangle2D[nCols][nRows];
int i = 0;
// Label regions from top left, rather than the transformed orientation
for (Interval column : columns) {
int j = 0;
for (Interval row : rows) {
regions[nCols - i - 1][nRows - j - 1] =
new Rectangle2D.Double(
column.start,
row.start,
column.end - column.start,
row.end - row.start);
++j;
}
++i;
}
return regions;
}
/**
* Register each character's bounding box, updating boxes field to maintain a list of all
* distinct groups of characters.
*
* <p>Overrides the default functionality of PDFTextStripper. Most of this is taken from
* DrawPrintTextLocations.java, with extra steps at end of main loop
*/
@Override
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
for (TextPosition text : textPositions) {
// glyph space -> user space
// note: text.getTextMatrix() is *not* the Text Matrix, it's the Text Rendering Matrix
AffineTransform at = text.getTextMatrix().createAffineTransform();
PDFont font = text.getFont();
BoundingBox bbox = font.getBoundingBox();
// advance width, bbox height (glyph space)
float xadvance =
font.getWidth(text.getCharacterCodes()[0]); // todo: should iterate all chars
Rectangle2D.Float rect =
new Rectangle2D.Float(0, bbox.getLowerLeftY(), xadvance, bbox.getHeight());
if (font instanceof PDType3Font) {
// bbox and font matrix are unscaled
at.concatenate(font.getFontMatrix().createAffineTransform());
} else {
// bbox and font matrix are already scaled to 1000
at.scale(1 / 1000f, 1 / 1000f);
}
Shape s = at.createTransformedShape(rect);
s = flipAT.createTransformedShape(s);
s = rotateAT.createTransformedShape(s);
//
// Merge character's bounding box with boxes field
//
Rectangle2D bounds = s.getBounds2D();
// Pad sides to detect almost touching boxes
Rectangle2D hitbox = bounds.getBounds2D();
hitbox.add(bounds.getMinX() - dx, bounds.getMinY() - dy);
hitbox.add(bounds.getMaxX() + dx, bounds.getMaxY() + dy);
// Find all overlapping boxes
List<Rectangle2D> intersectList = new ArrayList<Rectangle2D>();
for (Rectangle2D box : boxes) {
if (box.intersects(hitbox)) {
intersectList.add(box);
}
}
// Combine all touching boxes and update
// (NOTE: Potentially this could leave some overlapping boxes un-merged,
// but it's sufficient for now and get's fixed up in calculateTableRegions)
for (Rectangle2D box : intersectList) {
bounds.add(box);
boxes.remove(box);
}
boxes.add(bounds);
}
}
/**
* This method does nothing in this derived class, because beads and regions are incompatible.
* Beads are ignored when stripping by area.
*
* @param aShouldSeparateByBeads The new grouping of beads.
*/
@Override
public final void setShouldSeparateByBeads(boolean aShouldSeparateByBeads) {}
/** Adapted from PDFTextStripperByArea {@inheritDoc} */
@Override
protected void processTextPosition(TextPosition text) {
if (regionArea != null && !regionArea.contains(text.getX(), text.getY())) {
// skip character
} else {
super.processTextPosition(text);
}
}
}

View File

@@ -34,7 +34,9 @@ public class DatabaseWebController {
}
List<FileInfo> backupList = databaseBackupHelper.getBackupList();
model.addAttribute("systemUpdate", backupList);
model.addAttribute("backupFiles", backupList);
model.addAttribute("databaseVersion", databaseBackupHelper.getH2Version());
return "database";
}

View File

@@ -55,6 +55,11 @@ public class HomeWebController {
return "licenses";
}
@GetMapping("/releases")
public String getReleaseNotes(Model model) {
return "releases";
}
@GetMapping("/")
public String home(Model model) {
model.addAttribute("currentPage", "home");

View File

@@ -320,12 +320,20 @@ public class ApplicationProperties {
public static class SessionLimit {
private int libreOfficeSessionLimit;
private int pdfToHtmlSessionLimit;
private int ocrMyPdfSessionLimit;
private int pythonOpenCvSessionLimit;
private int ghostScriptSessionLimit;
private int weasyPrintSessionLimit;
private int installAppSessionLimit;
private int calibreSessionLimit;
private int qpdfSessionLimit;
private int tesseractSessionLimit;
public int getQpdfSessionLimit() {
return qpdfSessionLimit > 0 ? qpdfSessionLimit : 2;
}
public int getTesseractSessionLimit() {
return tesseractSessionLimit > 0 ? tesseractSessionLimit : 1;
}
public int getLibreOfficeSessionLimit() {
return libreOfficeSessionLimit > 0 ? libreOfficeSessionLimit : 1;
@@ -335,18 +343,10 @@ public class ApplicationProperties {
return pdfToHtmlSessionLimit > 0 ? pdfToHtmlSessionLimit : 1;
}
public int getOcrMyPdfSessionLimit() {
return ocrMyPdfSessionLimit > 0 ? ocrMyPdfSessionLimit : 2;
}
public int getPythonOpenCvSessionLimit() {
return pythonOpenCvSessionLimit > 0 ? pythonOpenCvSessionLimit : 8;
}
public int getGhostScriptSessionLimit() {
return ghostScriptSessionLimit > 0 ? ghostScriptSessionLimit : 16;
}
public int getWeasyPrintSessionLimit() {
return weasyPrintSessionLimit > 0 ? weasyPrintSessionLimit : 16;
}
@@ -364,12 +364,20 @@ public class ApplicationProperties {
public static class TimeoutMinutes {
private long libreOfficeTimeoutMinutes;
private long pdfToHtmlTimeoutMinutes;
private long ocrMyPdfTimeoutMinutes;
private long pythonOpenCvTimeoutMinutes;
private long ghostScriptTimeoutMinutes;
private long weasyPrintTimeoutMinutes;
private long installAppTimeoutMinutes;
private long calibreTimeoutMinutes;
private long tesseractTimeoutMinutes;
private long qpdfTimeoutMinutes;
public long getTesseractTimeoutMinutes() {
return tesseractTimeoutMinutes > 0 ? tesseractTimeoutMinutes : 30;
}
public long getQpdfTimeoutMinutes() {
return qpdfTimeoutMinutes > 0 ? qpdfTimeoutMinutes : 30;
}
public long getLibreOfficeTimeoutMinutes() {
return libreOfficeTimeoutMinutes > 0 ? libreOfficeTimeoutMinutes : 30;
@@ -379,18 +387,10 @@ public class ApplicationProperties {
return pdfToHtmlTimeoutMinutes > 0 ? pdfToHtmlTimeoutMinutes : 20;
}
public long getOcrMyPdfTimeoutMinutes() {
return ocrMyPdfTimeoutMinutes > 0 ? ocrMyPdfTimeoutMinutes : 30;
}
public long getPythonOpenCvTimeoutMinutes() {
return pythonOpenCvTimeoutMinutes > 0 ? pythonOpenCvTimeoutMinutes : 30;
}
public long getGhostScriptTimeoutMinutes() {
return ghostScriptTimeoutMinutes > 0 ? ghostScriptTimeoutMinutes : 30;
}
public long getWeasyPrintTimeoutMinutes() {
return weasyPrintTimeoutMinutes > 0 ? weasyPrintTimeoutMinutes : 30;
}

View File

@@ -2,5 +2,5 @@ package stirling.software.SPDF.model;
public enum AuthenticationType {
WEB,
OAUTH2
SSO
}

View File

@@ -18,4 +18,15 @@ public class OptimizePdfRequest extends PDFFile {
@Schema(description = "The expected output size, e.g. '100MB', '25KB', etc.")
private String expectedOutputSize;
@Schema(
description = "Whether to linearize the PDF for faster web viewing. Default is false.",
defaultValue = "false")
private Boolean linearize = false;
@Schema(
description =
"Whether to normalize the PDF content for better compatibility. Default is true.",
defaultValue = "true")
private Boolean normalize = true;
}

View File

@@ -15,18 +15,6 @@ public class ProcessPdfWithOcrRequest extends PDFFile {
@Schema(description = "List of languages to use in OCR processing")
private List<String> languages;
@Schema(description = "Include OCR text in a sidecar text file if set to true")
private boolean sidecar;
@Schema(description = "Deskew the input file if set to true")
private boolean deskew;
@Schema(description = "Clean the input file if set to true")
private boolean clean;
@Schema(description = "Clean the final output if set to true")
private boolean cleanFinal;
@Schema(
description = "Specify the OCR type, e.g., 'skip-text', 'force-ocr', or 'Normal'",
allowableValues = {"skip-text", "force-ocr", "Normal"})
@@ -37,7 +25,4 @@ public class ProcessPdfWithOcrRequest extends PDFFile {
allowableValues = {"hocr", "sandwich"},
defaultValue = "hocr")
private String ocrRenderType = "hocr";
@Schema(description = "Remove images from the output PDF if set to true")
private boolean removeImagesAfter;
}

View File

@@ -0,0 +1,16 @@
package stirling.software.SPDF.pdf;
import org.apache.commons.csv.CSVFormat;
import technology.tabula.writers.CSVWriter;
public class FlexibleCSVWriter extends CSVWriter {
public FlexibleCSVWriter() {
super();
}
public FlexibleCSVWriter(CSVFormat csvFormat) {
super(csvFormat);
}
}

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
@@ -19,4 +20,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Optional<User> findByApiKey(String apiKey);
List<User> findByAuthenticationTypeIgnoreCase(String authenticationType);
}

View File

@@ -24,7 +24,7 @@ public class MetricsAggregatorService {
this.postHogService = postHogService;
}
@Scheduled(fixedRate = 900000) // Run every 15 minutes
@Scheduled(fixedRate = 1800000) // Run every 30 minutes
public void aggregateAndSendMetrics() {
Map<String, Object> metrics = new HashMap<>();
Search.in(meterRegistry)
@@ -34,17 +34,22 @@ public class MetricsAggregatorService {
counter -> {
String method = counter.getId().getTag("method");
String uri = counter.getId().getTag("uri");
// Skip if either method or uri is null
if (method == null || uri == null) {
return;
}
String key = String.format(
"http_requests_%s_%s",
method,
uri.replace("/", "_")
);
if (!method.equals("GET") && !method.equals("POST")) {
return;
}
// Skip URIs that are 2 characters or shorter
if (uri.length() <= 2) {
return;
}
String key =
String.format(
"http_requests_%s_%s", method, uri.replace("/", "_"));
double currentCount = counter.count();
double lastCount = lastSentMetrics.getOrDefault(key, 0.0);

View File

@@ -17,15 +17,18 @@ public class PdfMetadataService {
private final ApplicationProperties applicationProperties;
private final String stirlingPDFLabel;
private final UserServiceInterface userService;
private final boolean runningEE;
@Autowired
public PdfMetadataService(
ApplicationProperties applicationProperties,
@Qualifier("StirlingPDFLabel") String stirlingPDFLabel,
@Qualifier("runningEE") boolean runningEE,
@Autowired(required = false) UserServiceInterface userService) {
this.applicationProperties = applicationProperties;
this.stirlingPDFLabel = stirlingPDFLabel;
this.userService = userService;
this.runningEE = runningEE;
}
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
@@ -61,10 +64,8 @@ public class PdfMetadataService {
String creator = stirlingPDFLabel;
if (applicationProperties
.getEnterpriseEdition()
.getCustomMetadata()
.isAutoUpdateMetadata()) {
if (applicationProperties.getEnterpriseEdition().getCustomMetadata().isAutoUpdateMetadata()
&& runningEE) {
creator = applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator();
pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
@@ -83,10 +84,8 @@ public class PdfMetadataService {
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
String author = pdfMetadata.getAuthor();
if (applicationProperties
.getEnterpriseEdition()
.getCustomMetadata()
.isAutoUpdateMetadata()) {
if (applicationProperties.getEnterpriseEdition().getCustomMetadata().isAutoUpdateMetadata()
&& runningEE) {
author = applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor();
if (userService != null) {

View File

@@ -15,6 +15,7 @@ import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import com.posthog.java.PostHog;
@@ -26,19 +27,25 @@ import stirling.software.SPDF.model.ApplicationProperties;
public class PostHogService {
private final PostHog postHog;
private final String uniqueId;
private final String appVersion;
private final ApplicationProperties applicationProperties;
private final UserServiceInterface userService;
private final Environment env;
@Autowired
public PostHogService(
PostHog postHog,
@Qualifier("UUID") String uuid,
@Qualifier("appVersion") String appVersion,
ApplicationProperties applicationProperties,
@Autowired(required = false) UserServiceInterface userService) {
@Autowired(required = false) UserServiceInterface userService,
Environment env) {
this.postHog = postHog;
this.uniqueId = uuid;
this.appVersion = appVersion;
this.applicationProperties = applicationProperties;
this.userService = userService;
this.env = env;
captureSystemInfo();
}
@@ -64,6 +71,16 @@ public class PostHogService {
Map<String, Object> metrics = new HashMap<>();
try {
// Application version
metrics.put("app_version", appVersion);
String deploymentType = "JAR"; // default
if ("true".equalsIgnoreCase(env.getProperty("BROWSER_OPEN"))) {
deploymentType = "EXE";
} else if (isRunningInDocker()) {
deploymentType = "DOCKER";
}
metrics.put("deployment_type", deploymentType);
// System info
metrics.put("os_name", System.getProperty("os.name"));
metrics.put("os_version", System.getProperty("os.version"));
@@ -132,7 +149,6 @@ public class PostHogService {
// Docker detection and stats
boolean isDocker = isRunningInDocker();
metrics.put("is_docker", isDocker);
if (isDocker) {
metrics.put("docker_metrics", getDockerMetrics());
}

View File

@@ -105,7 +105,7 @@ public class FileToPdf {
new ByteArrayInputStream(Files.readAllBytes(zipFilePath)))) {
ZipEntry entry = zipIn.getNextEntry();
while (entry != null) {
Path filePath = tempUnzippedDir.resolve(entry.getName());
Path filePath = tempUnzippedDir.resolve(sanitizeZipFilename(entry.getName()));
if (!entry.isDirectory()) {
Files.createDirectories(filePath.getParent());
if (entry.getName().toLowerCase().endsWith(".html")
@@ -175,7 +175,7 @@ public class FileToPdf {
ZipSecurity.createHardenedInputStream(new ByteArrayInputStream(fileBytes))) {
ZipEntry entry = zipIn.getNextEntry();
while (entry != null) {
Path filePath = tempDirectory.resolve(entry.getName());
Path filePath = tempDirectory.resolve(sanitizeZipFilename(entry.getName()));
if (entry.isDirectory()) {
Files.createDirectories(filePath); // Explicitly create the directory structure
} else {
@@ -241,4 +241,14 @@ public class FileToPdf {
Files.deleteIfExists(tempOutputFile);
}
}
static String sanitizeZipFilename(String entryName) {
if (entryName == null || entryName.trim().isEmpty()) {
return entryName;
}
while (entryName.contains("../") || entryName.contains("..\\")) {
entryName = entryName.replace("../", "").replace("..\\", "");
}
return entryName;
}
}

View File

@@ -29,12 +29,12 @@ public class ProcessExecutor {
public enum Processes {
LIBRE_OFFICE,
PDFTOHTML,
OCR_MY_PDF,
PYTHON_OPENCV,
GHOSTSCRIPT,
WEASYPRINT,
INSTALL_APP,
CALIBRE
CALIBRE,
TESSERACT,
QPDF
}
private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>();
@@ -59,21 +59,11 @@ public class ProcessExecutor {
.getProcessExecutor()
.getSessionLimit()
.getPdfToHtmlSessionLimit();
case OCR_MY_PDF ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getOcrMyPdfSessionLimit();
case PYTHON_OPENCV ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getPythonOpenCvSessionLimit();
case GHOSTSCRIPT ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getGhostScriptSessionLimit();
case WEASYPRINT ->
applicationProperties
.getProcessExecutor()
@@ -84,6 +74,16 @@ public class ProcessExecutor {
.getProcessExecutor()
.getSessionLimit()
.getInstallAppSessionLimit();
case TESSERACT ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getTesseractSessionLimit();
case QPDF ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getQpdfSessionLimit();
case CALIBRE ->
applicationProperties
.getProcessExecutor()
@@ -103,21 +103,11 @@ public class ProcessExecutor {
.getProcessExecutor()
.getTimeoutMinutes()
.getPdfToHtmlTimeoutMinutes();
case OCR_MY_PDF ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getOcrMyPdfTimeoutMinutes();
case PYTHON_OPENCV ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getPythonOpenCvTimeoutMinutes();
case GHOSTSCRIPT ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getGhostScriptTimeoutMinutes();
case WEASYPRINT ->
applicationProperties
.getProcessExecutor()
@@ -128,6 +118,16 @@ public class ProcessExecutor {
.getProcessExecutor()
.getTimeoutMinutes()
.getInstallAppTimeoutMinutes();
case TESSERACT ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getTesseractTimeoutMinutes();
case QPDF ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getQpdfTimeoutMinutes();
case CALIBRE ->
applicationProperties
.getProcessExecutor()

View File

@@ -0,0 +1,26 @@
package stirling.software.SPDF.utils.propertyeditor;
import java.beans.PropertyEditorSupport;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class StringToMapPropertyEditor extends PropertyEditorSupport {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
TypeReference<HashMap<String, String>> typeRef =
new TypeReference<HashMap<String, String>>() {};
Map<String, String> map = objectMapper.readValue(text, typeRef);
setValue(map);
} catch (Exception e) {
throw new IllegalArgumentException(
"Failed to convert java.lang.String to java.util.Map");
}
}
}

View File

@@ -3,6 +3,10 @@ multipart.enabled=true
logging.level.org.springframework=WARN
logging.level.org.hibernate=WARN
logging.level.org.eclipse.jetty=WARN
#logging.level.org.springframework.security.saml2=TRACE
#logging.level.org.springframework.security=DEBUG
#logging.level.org.opensaml=DEBUG
#logging.level.stirling.software.SPDF.config.security: DEBUG
logging.level.com.zaxxer.hikari=WARN
spring.jpa.open-in-view=false
@@ -27,6 +31,8 @@ server.servlet.context-path=${SYSTEM_ROOTURIPATH:/}
spring.devtools.restart.enabled=true
spring.devtools.livereload.enabled=true
spring.devtools.restart.exclude=stirling.software.SPDF.config.security/**
spring.thymeleaf.encoding=UTF-8
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
@@ -35,7 +41,7 @@ spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
#spring.thymeleaf.cache=false
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

View File

@@ -81,11 +81,11 @@ page=صفحة
pages=صفحات
loading=جارٍ التحميل...
addToDoc=إضافة إلى المستند
reset=Reset
reset=إعداة ضبط
legal.privacy=سياسة الخصوصية
legal.terms=شروط الاستخدام
legal.accessibility=Accessibility
legal.accessibility=إمكانية الوصول
legal.cookie=سياسة ملفات تعريف الارتباط
legal.impressum=بيان الهوية
@@ -119,8 +119,8 @@ pipelineOptions.validateButton=تحقق
########################
enterpriseEdition.button=ترقية إلى محترف
enterpriseEdition.warning=هذه الخاصية متوفرة فقط للمستخدمين المحترفين.
enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
enterpriseEdition.yamlAdvert=يدعم Stirling PDF Pro ملفات الإعدادات YAML وميزات SSO أخرى
enterpriseEdition.ssoAdvert=هل تبحث عن المزيد من ميزات إدارة المستخدمين؟ اطلع على Stirling PDF Pro
#################
@@ -142,7 +142,7 @@ navbar.language=اللغات
navbar.settings=إعدادات
navbar.allTools=أدوات
navbar.multiTool=أدوات متعددة
navbar.search=Search
navbar.search=البحث
navbar.sections.organize=تنظيم
navbar.sections.convertTo=تحويل الى PDF
navbar.sections.convertFrom=تحويل من PDF
@@ -247,8 +247,8 @@ database.fileNotFound=لم يتم العثور على الملف
database.fileNullOrEmpty=يجب ألا يكون الملف فارغًا أو خاليًا
database.failedImportFile=فشل استيراد الملف
session.expired=Your session has expired. Please refresh the page and try again.
session.refreshPage=Refresh Page
session.expired=لقد انتهت جلستك. يرجى تحديث الصفحة والمحاولة مرة أخرى
session.refreshPage=تحديث الصفحة
#############
# HOME-PAGE #
@@ -513,15 +513,15 @@ home.splitPdfByChapters.desc=قسم مستند PDF إلى ملفات متعدد
splitPdfByChapters.tags=تجزئة، فصول، علامات تبويب، تنظيم
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=استبدال-إلغاء مirro لون PDF
home.replaceColorPdf.title=Replace and Invert Color
replace-color.title=إستبدال-عكس اللون
replace-color.header=استبدال-عكس لون PDF
home.replaceColorPdf.title=إستبدال و عكس الألوان
home.replaceColorPdf.desc=استبدال الألوان للنصوص والخلفيات في المستندات PDF وإلغاء تعكير اللون الكامل للمستند لتقليل حجم الملف
replaceColorPdf.tags=استبدال اللون، عمليات الصفحة، الخلفية، جانب الخادم
replace-color.selectText.1=خيارات استبدال-إلغاء مirro لون
replace-color.selectText.2=Default(Default high contrast colors)
replace-color.selectText.1=خيارات استبدال أو عكس الألوان
replace-color.selectText.2=افتراضي(ألوان التباين العالي الافتراضية)
replace-color.selectText.3=خصيصة (ألوان شخصية)
replace-color.selectText.4=Full-Invert(Invert all colors)
replace-color.selectText.4=عكس كامل(عكس جميع الألوان)
replace-color.selectText.5=خيارات ألوان التباين العالي
replace-color.selectText.6=نص أبيض على خلفية سوداء
replace-color.selectText.7=نص أسود على خلفية بيضاء
@@ -818,7 +818,12 @@ sign.save=حفظ توقيع
sign.personalSigs=توقيعات شخصية
sign.sharedSigs=توقيعات مشتركة
sign.noSavedSigs=لم يتم العثور على توقيعات محفوظة
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=إصلاح
@@ -863,7 +868,7 @@ ocr.selectText.10=وضع التعرف الضوئي على الحروف
ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل)
ocr.selectText.12=نوع العرض (متقدم)
ocr.help=يرجى قراءة هذه الوثائق حول كيفية استخدام هذا للغات أخرى و/أو الاستخدام ليس في Docker
ocr.credit=تستخدم هذه الخدمة OCRmyPDF و Tesseract للتعرف الضوئي على الحروف.
ocr.credit=تستخدم هذه الخدمة qpdf و Tesseract للتعرف الضوئي على الحروف.
ocr.submit=معالجة PDF باستخدام OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=تحويل إلى PDF
#compress
compress.title=ضغط
compress.header=ضغط ملف PDF
compress.credit=تستخدم هذه الخدمة OCRmyPDF لضغط / تحسين PDF.
compress.credit=تستخدم هذه الخدمة qpdf لضغط / تحسين PDF.
compress.selectText.1=الوضع اليدوي - من 1 إلى 4
compress.selectText.2=مستوى التحسين:
compress.selectText.3=4 (رهيب للصور النصية)
@@ -935,17 +940,29 @@ pdfOrganiser.placeholder=(مثال: 1,3,2 أو 4-8,2,10-12 أو 2n-1)
multiTool.title=أداة متعددة PDF
multiTool.header=أداة متعددة PDF
multiTool.uploadPrompts=اسم الملف
multiTool.selectAll=Select All
multiTool.deselectAll=Deselect All
multiTool.selectPages=Page Select
multiTool.selectedPages=Selected Pages
multiTool.page=Page
multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.selectAll=تحديد الكل
multiTool.deselectAll=إلغاء تحديد الكل
multiTool.selectPages=تحديد الصفحة
multiTool.selectedPages=الصفحات المحددة
multiTool.page=صفحة
multiTool.deleteSelected=حذف المحدد
multiTool.downloadAll=تصدير
multiTool.downloadSelected=تصدير المحدد
multiTool.insertPageBreak=إدراج فاصل صفحات
multiTool.addFile=إضافة ملف
multiTool.rotateLeft=تدوير إلى اليسار
multiTool.rotateRight=تدوير إلى اليمين
multiTool.split=تقسيم
multiTool.moveLeft=تحريك إلى اليسار
multiTool.moveRight=تحريك إلى اليمين
multiTool.delete=حذف
multiTool.dragDropMessage=الصفحات المحددة
multiTool.undo=تراجع
multiTool.redo=إعادة إجراء
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
#view pdf
viewPdf.title=عرض PDF
@@ -1097,7 +1114,7 @@ changeMetadata.submit=تغيير
#pdfToPDFA
pdfToPDFA.title=PDF إلى PDF/A
pdfToPDFA.header=PDF إلى PDF/A
pdfToPDFA.credit=تستخدم هذه الخدمة ghostscript لتحويل PDF/A.
pdfToPDFA.credit=تستخدم هذه الخدمة qpdf لتحويل PDF/A.
pdfToPDFA.submit=تحويل
pdfToPDFA.tip=لا يعمل حاليًا لمدخلات متعددة في وقت واحد
pdfToPDFA.outputFormat=تنسيق الإخراج
@@ -1239,9 +1256,22 @@ splitByChapters.title=تجزئة المستند حسب الفصول
splitByChapters.header=تجزئة المستند حسب الفصول
splitByChapters.bookmarkLevel=مستوى العلامات التذكارية
splitByChapters.includeMetadata=شامل البيانات المرفقة
splitByChapters.allowDuplicates=Allow Duplicates
splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
splitByChapters.allowDuplicates=السماح بالتكرار
splitByChapters.desc.1=هذه الأداة تقوم بتقسيم ملف PDF إلى عدة ملفات PDF استناداً إلى بنية فصوله
splitByChapters.desc.2=مستوى الإشارة المرجعية: اختر مستوى الإشارات المرجعية التي تريد استخدامها للتقسيم (0 للمستوى الأعلى، 1 للمستوى الثاني، وما إلى ذلك)
splitByChapters.desc.3=تمثيل البيانات الأصلية: إذا تم اختيارها، سترمز البيانات المرجعية الأصلية إلى كل PDF مجزأ.
splitByChapters.desc.4=سماح بالتكرار: إذا تم اختياره، يسمح بوجود معاينات متعددة في الصفحة نفسها لخلق ملفات PDF منفصلة.
splitByChapters.submit=تقطيع ملف PDF
#File Chooser
fileChooser.click=انقر هنا
fileChooser.or=أو
fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

File diff suppressed because it is too large Load Diff

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Поправи
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR режим
ocr.selectText.11=Премахване на изображения след OCR (Премахва ВСИЧКИ изображения, полезно само ако е част от стъпката на преобразуване)
ocr.selectText.12=Тип изобразяване (Разширен)
ocr.help=Моля, прочетете тази документация за това как да използвате това за други езици и/или да не използвате в docker
ocr.credit=Тази услуга използва OCRmyPDF и Tesseract за OCR.
ocr.credit=Тази услуга използва qpdf и Tesseract за OCR.
ocr.submit=Обработка на PDF чрез OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Преобразуване към PDF
#compress
compress.title=Компресиране
compress.header=Компресиране на PDF
compress.credit=Тази услуга използва Ghostscript за PDF компресиране/оптимизиране.
compress.credit=Тази услуга използва qpdf за PDF компресиране/оптимизиране.
compress.selectText.1=Ръчен режим - от 1 до 4
compress.selectText.2=Ниво на оптимизация:
compress.selectText.3=4 (Ужасно за текстови изображения)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Промени
#pdfToPDFA
pdfToPDFA.title=PDF към PDF/A
pdfToPDFA.header=PDF към PDF/A
pdfToPDFA.credit=Тази услуга използва ghostscript за PDF/A преобразуване.
pdfToPDFA.credit=Тази услуга използва qpdf за PDF/A преобразуване.
pdfToPDFA.submit=Преобразуване
pdfToPDFA.tip=В момента не работи за няколко входа наведнъж
pdfToPDFA.outputFormat=Изходен формат
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Ниво на отметка: Изберете нивот
splitByChapters.desc.3=Включване на метаданни: Ако е отметнато, метаданните на оригиналния PDF ще бъдат включени във всеки разделен PDF.
splitByChapters.desc.4=Разрешаване на дубликати: Ако е отметнато, позволява множество отметки на една и съща страница за създаване на отделни PDF файлове.
splitByChapters.submit=Разделяне на PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Desa Signatura
sign.personalSigs=Signatures Personals
sign.sharedSigs=Signatures Compartides
sign.noSavedSigs=No s'han trobat signatures desades
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparar
@@ -863,7 +868,7 @@ ocr.selectText.10=Mode OCR
ocr.selectText.11=Elimina Imatges després de l'OCR (Elimina TOTES les imatges, útil si forma part d'un procés de conversió)
ocr.selectText.12=Tipus de Renderització (Avançat)
ocr.help=Llegeix aquesta documentació sobre com utilitzar-la per a altres idiomes i/o no utilitzar-la a Docker
ocr.credit=Aquest servei fa servir OCRmyPDF i Tesseract per a OCR.
ocr.credit=Aquest servei fa servir qpdf i Tesseract per a OCR.
ocr.submit=Processa PDF amb OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Converteix a PDF
#compress
compress.title=Comprimir
compress.header=Comprimir PDF
compress.credit=Aquest servei utilitza Ghostscript per a la compressió/optimització de PDF.
compress.credit=Aquest servei utilitza qpdf per a la compressió/optimització de PDF.
compress.selectText.1=Mode manual: de l'1 al 4
compress.selectText.2=Nivell d'optimització:
compress.selectText.3=4 (terrible per a imatges de text)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Canvia
#pdfToPDFA
pdfToPDFA.title=PDF a PDF/A
pdfToPDFA.header=PDF a PDF/A
pdfToPDFA.credit=Utilitza Ghostscript per a la conversió a PDF/A
pdfToPDFA.credit=Utilitza qpdf per a la conversió a PDF/A
pdfToPDFA.submit=Converteix
pdfToPDFA.tip=Actualment no funciona per a múltiples entrades al mateix temps
pdfToPDFA.outputFormat=Format de sortida
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Nivell de Marcadors: Tria el nivell de marcadors que s'ut
splitByChapters.desc.3=Incloure Metadades: Si està marcat, les metadades del PDF original s'inclouran en cada PDF dividit.
splitByChapters.desc.4=Permetre Duplicats: Si està marcat, permet diversos marcadors a la mateixa pàgina per crear PDFs separats.
splitByChapters.submit=Divideix PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Uložit podpis
sign.personalSigs=Osobní podpisy
sign.sharedSigs=Sdílené podpisy
sign.noSavedSigs=Nenašly se žádné uložené podpisy
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Opravit
@@ -863,7 +868,7 @@ ocr.selectText.10=Režim OCR
ocr.selectText.11=Odstranit obrázky po OCR (Odstraní VŠECHNY obrázky, užitečné pouze jako součást kroku konverze)
ocr.selectText.12=Typ vykreslení (Pokročilé)
ocr.help=Prosím, přečtěte si tuto dokumentaci o použití pro jiné jazyky a/nebo použití mimo Docker
ocr.credit=Tato služba používá OCRmyPDF a Tesseract pro OCR.
ocr.credit=Tato služba používá qpdf a Tesseract pro OCR.
ocr.submit=Zpracovat PDF s OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Převést na PDF
#compress
compress.title=Komprese
compress.header=Komprimovat PDF
compress.credit=Tato služba používá Ghostscript pro kompresi/optimalizaci PDF.
compress.credit=Tato služba používá qpdf pro kompresi/optimalizaci PDF.
compress.selectText.1=Ruční režim - Od 1 do 4
compress.selectText.2=Úroveň optimalizace:
compress.selectText.3=4 (Hrozné pro textové obrázky)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Změnit
#pdfToPDFA
pdfToPDFA.title=PDF na PDF/A
pdfToPDFA.header=PDF na PDF/A
pdfToPDFA.credit=Tato služba používá ghostscript pro konverzi do formátu PDF/A
pdfToPDFA.credit=Tato služba používá qpdf pro konverzi do formátu PDF/A
pdfToPDFA.submit=Převést
pdfToPDFA.tip=V současné době nepracuje pro více vstupů najednou
pdfToPDFA.outputFormat=Výstupní formát
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Úroveň záhlaví: Zvolte úroveň záhlaví pro použit
splitByChapters.desc.3=Zahrnout metadatů: Pokud je zaškrtnuto, původní metadata PDF souboru budou zahrnuty do každého odděleného PDF souboru.
splitByChapters.desc.4=Povolit duplicitní záznamy: Pokud je zaškrtnuto, návštěvníci mohou vytvořit samostatné PDF soubory z více záhlaví na stejné straně.
splitByChapters.submit=Podělit se PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Gem Signatur
sign.personalSigs=Personlige Signaturer
sign.sharedSigs=Delte Signaturer
sign.noSavedSigs=Ingen Gemte Signaturer Fundet
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparér
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR-tilstand
ocr.selectText.11=Fjern billeder efter OCR (Fjerner ALLE billeder, kun nyttigt hvis det er en del af konverteringstrinnet)
ocr.selectText.12=Renderingstype (Avanceret)
ocr.help=Læs venligst denne dokumentation om, hvordan man bruger dette til andre sprog og/eller brug uden for docker
ocr.credit=Denne tjeneste bruger OCRmyPDF og Tesseract til OCR.
ocr.credit=Denne tjeneste bruger qpdf og Tesseract til OCR.
ocr.submit=Behandl PDF med OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Konvertér til PDF
#compress
compress.title=Komprimer
compress.header=Komprimer PDF
compress.credit=Denne tjeneste bruger Ghostscript til PDF Komprimering/Optimering.
compress.credit=Denne tjeneste bruger qpdf til PDF Komprimering/Optimering.
compress.selectText.1=Manuel Tilstand - Fra 1 til 4
compress.selectText.2=Optimeringsniveau:
compress.selectText.3=4 (Forfærdelig for tekstbilleder)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Ændre
#pdfToPDFA
pdfToPDFA.title=PDF Til PDF/A
pdfToPDFA.header=PDF Til PDF/A
pdfToPDFA.credit=Denne tjeneste bruger ghostscript til PDF/A-konvertering
pdfToPDFA.credit=Denne tjeneste bruger qpdf til PDF/A-konvertering
pdfToPDFA.submit=Konvertér
pdfToPDFA.tip=Fungerer i øjeblikket ikke for flere input på én gang
pdfToPDFA.outputFormat=Outputformat
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bogmærke niveau: Vælg nivået af bogmærker, der skal b
splitByChapters.desc.3=Inkluder metadata: Hvis markeret, vil den originale PDF's metadata inkluderes i hver splitterdels PDF.
splitByChapters.desc.4=Tillad duplikater: Hvis markeret, tillader det flere bogmærker på samme side til at oprette separate PDF'er.
splitByChapters.submit=Splitter PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -81,7 +81,7 @@ page=Seite
pages=Seiten
loading=Laden...
addToDoc=In Dokument hinzufügen
reset=Reset
reset=Zurücksetzen
legal.privacy=Datenschutz
legal.terms=AGB
@@ -818,7 +818,12 @@ sign.save=Signature speichern
sign.personalSigs=Persönliche Signaturen
sign.sharedSigs=Geteilte Signaturen
sign.noSavedSigs=Es wurden keine gespeicherten Signaturen gefunden
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparieren
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR-Modus
ocr.selectText.11=Bilder nach OCR entfernen (Entfernt ALLE Bilder, nur sinnvoll, wenn Teil des Konvertierungsschritts)
ocr.selectText.12=Rendertyp (Erweitert)
ocr.help=Bitte lesen Sie diese Dokumentation, um zu erfahren, wie Sie dies für andere Sprachen verwenden und/oder nicht in Docker verwenden können
ocr.credit=Dieser Dienst verwendet OCRmyPDF und Tesseract für OCR.
ocr.credit=Dieser Dienst verwendet qpdf und Tesseract für OCR.
ocr.submit=PDF mit OCR verarbeiten
@@ -887,7 +892,7 @@ fileToPDF.submit=In PDF konvertieren
#compress
compress.title=Komprimieren
compress.header=PDF komprimieren
compress.credit=Dieser Dienst verwendet Ghostscript für die PDF-Komprimierung/-Optimierung.
compress.credit=Dieser Dienst verwendet qpdf für die PDF-Komprimierung/-Optimierung.
compress.selectText.1=Manueller Modus Von 1 bis 4
compress.selectText.2=Optimierungsstufe:
compress.selectText.3=4 (Schrecklich für Textbilder)
@@ -944,8 +949,20 @@ multiTool.deleteSelected=Auswahl löschen
multiTool.downloadAll=Downloaden
multiTool.downloadSelected=Auswahl downloaden
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
multiTool-advert.message=Diese Funktion ist auch auf unserer <a href="{0}">PDF-Multitool-Seite</a> verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen!
#view pdf
viewPdf.title=PDF anzeigen
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Ändern
#pdfToPDFA
pdfToPDFA.title=PDF zu PDF/A
pdfToPDFA.header=PDF zu PDF/A
pdfToPDFA.credit=Dieser Dienst verwendet ghostscript für die PDF/A-Konvertierung
pdfToPDFA.credit=Dieser Dienst verwendet qpdf für die PDF/A-Konvertierung
pdfToPDFA.submit=Konvertieren
pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten.
pdfToPDFA.outputFormat=Ausgabeformat
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Lesezeichenebene: Wählen Sie die Ebene der Lesezeichen,
splitByChapters.desc.3=Metadaten einschließen: Wenn diese Option aktiviert ist, werden die Metadaten der ursprünglichen PDF-Datei in jede aufgeteilte PDF-Datei übernommen.
splitByChapters.desc.4=Duplikate erlauben: Wenn diese Option aktiviert ist, können mehrere Lesezeichen auf derselben Seite separate PDF Dateien erstellen.
splitByChapters.submit=PDF teilen
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Αποθήκευση Αλιάσης
sign.personalSigs=Προσωπικές Αλιάσεις
sign.sharedSigs=Μεταδότες Αλιάσεις
sign.noSavedSigs=Δεν βρέθηκαν αποθηκευμένες αλιάσεις
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Επιδιόρθωση
@@ -863,7 +868,7 @@ ocr.selectText.10=Λειτουργία OCR
ocr.selectText.11=Κατάργηση εικόνων μετά το OCR (Καταργεί ΟΛΕΣ τις εικόνες, είναι χρήσιμο μόνο αν αποτελεί μέρος του βήματος μετατροπής)
ocr.selectText.12=Τύπος απόδοσης (Για προχωρημένους)
ocr.help=Διαβάστε αυτήν την τεκμηρίωση σχετικά με τον τρόπο χρήσης αυτής για άλλες γλώσσες ή/και μη χρήσης σε docker
ocr.credit=Αυτή η υπηρεσία χρησιμοποιεί OCRmyPDF και Tesseract για OCR.
ocr.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf και Tesseract για OCR.
ocr.submit=Επεξεργασία PDF με OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Μετατροπή σε PDF
#compress
compress.title=Συμπίεση
compress.header=Συμπίεση PDF
compress.credit=Αυτή η υπηρεσία χρησιμοποιεί Ghostscript για PDF Συμπίεση/Βελτιστοποίηση.
compress.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για PDF Συμπίεση/Βελτιστοποίηση.
compress.selectText.1=Χειροκίνητη Λειτουργία - Από 1 έως 4
compress.selectText.2=Επίπεδο Βελτιστοποίησης:
compress.selectText.3=4 (Πολύ κακό για εικόνες κειμένου)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Επιλέξτε PDF για την προσθήκη το
watermark.selectText.2=Κείμενο Υδατογραφήματος:
watermark.selectText.3=Μέγεθος Κειμένου:
watermark.selectText.4=Περιστροφή (0-360):
watermark.selectText.5=widthSpacer (Κενό μεταξύ κάθε υδατογραφήματος οριζόντια):
watermark.selectText.6=heightSpacer (Κενό μεταξύ κάθε υδατογραφήματος κάθετα):
watermark.selectText.5=Width Spacer (Κενό μεταξύ κάθε υδατογραφήματος οριζόντια):
watermark.selectText.6=Height Spacer (Κενό μεταξύ κάθε υδατογραφήματος κάθετα):
watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%):
watermark.selectText.8=Τύπος Υδατογραφήματος:
watermark.selectText.9=Εικόνα Υδατογραφήματος:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Αλλαγή
#pdfToPDFA
pdfToPDFA.title=PDF σε PDF/A
pdfToPDFA.header=PDF σε PDF/A
pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί ghostscript για PDF/A μετατροπή
pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για PDF/A μετατροπή
pdfToPDFA.submit=Μετατροπή
pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα
pdfToPDFA.outputFormat=Εξόδος αναμορφώσεων
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Επίπεδο Σήμανσης Σκέψης: Επιλέ
splitByChapters.desc.3=Πρόσθεση Metadata: Αν επεξεργαστείται, οι αρχικές metadata του PDF θα προστεθούν σε κάθε διαλυμένο PDF.
splitByChapters.desc.4=Διάλυση Παρόντων Τίτλων Επιπέδου: Αν επεξεργαστείται, επιτρέπει τη δημιουργία αποκοπών PDF με βάση πλήρως καθορισμένους σήμαντες έδρας.
splitByChapters.submit=Διαλύστε το PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Repair
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR Mode
ocr.selectText.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step)
ocr.selectText.12=Render Type (Advanced)
ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker
ocr.credit=This service uses OCRmyPDF and Tesseract for OCR.
ocr.credit=This service uses qpdf and Tesseract for OCR.
ocr.submit=Process PDF with OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Convert to PDF
#compress
compress.title=Compress
compress.header=Compress PDF
compress.credit=This service uses Ghostscript for PDF Compress/Optimisation.
compress.credit=This service uses qpdf for PDF Compress/Optimisation.
compress.selectText.1=Manual Mode - From 1 to 4
compress.selectText.2=Optimization level:
compress.selectText.3=4 (Terrible for text images)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Select PDF to add watermark to:
watermark.selectText.2=Watermark Text:
watermark.selectText.3=Font Size:
watermark.selectText.4=Rotation (0-360):
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.selectText.5=Width Spacer (Space between each watermark horizontally):
watermark.selectText.6=Height Spacer (Space between each watermark vertically):
watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Change
#pdfToPDFA
pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=PDF To PDF/A
pdfToPDFA.credit=This service uses ghostscript for PDF/A conversion
pdfToPDFA.credit=This service uses qpdf for PDF/A conversion
pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Repair
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR Mode
ocr.selectText.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step)
ocr.selectText.12=Render Type (Advanced)
ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker
ocr.credit=This service uses OCRmyPDF and Tesseract for OCR.
ocr.credit=This service uses qpdf and Tesseract for OCR.
ocr.submit=Process PDF with OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Convert to PDF
#compress
compress.title=Compress
compress.header=Compress PDF
compress.credit=This service uses Ghostscript for PDF Compress/Optimisation.
compress.credit=This service uses qpdf for PDF Compress/Optimisation.
compress.selectText.1=Manual Mode - From 1 to 4
compress.selectText.2=Optimization level:
compress.selectText.3=4 (Terrible for text images)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Select PDF to add watermark to:
watermark.selectText.2=Watermark Text:
watermark.selectText.3=Font Size:
watermark.selectText.4=Rotation (0-360):
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.selectText.5=Width Spacer (Space between each watermark horizontally):
watermark.selectText.6=Height Spacer (Space between each watermark vertically):
watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Change
#pdfToPDFA
pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=PDF To PDF/A
pdfToPDFA.credit=This service uses ghostscript for PDF/A conversion
pdfToPDFA.credit=This service uses qpdf for PDF/A conversion
pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Guardar Firma
sign.personalSigs=Firmas Personales
sign.sharedSigs=Firmas compartidas
sign.noSavedSigs=No se encontraron firmas guardadas
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparar
@@ -863,7 +868,7 @@ ocr.selectText.10=Modo OCR
ocr.selectText.11=Eliminar imágenes después de OCR (Elimina TODAS las imágenes, solo es útil si es parte del paso de conversión)
ocr.selectText.12=Tipo de procesamiento (avanzado)
ocr.help=Lea esta documentación sobre cómo usar esto para otros idiomas y/o no usarlo en Docker
ocr.credit=Este servicio utiliza OCRmyPDF y Tesseract para OCR
ocr.credit=Este servicio utiliza qpdf y Tesseract para OCR
ocr.submit=Procesar PDF con OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Convertir a PDF
#compress
compress.title=Comprimir
compress.header=Comprimir PDF
compress.credit=Este servicio utiliza Ghostscript para compresión/optimización de PDF
compress.credit=Este servicio utiliza qpdf para compresión/optimización de PDF
compress.selectText.1=Modo manual - De 1 a 4
compress.selectText.2=Nivel de optimización:
compress.selectText.3=4 (Terrible para imágenes de texto)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Cambiar
#pdfToPDFA
pdfToPDFA.title=PDF a PDF/A
pdfToPDFA.header=PDF a PDF/A
pdfToPDFA.credit=Este servicio usa ghostscript para la conversión a PDF/A
pdfToPDFA.credit=Este servicio usa qpdf para la conversión a PDF/A
pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez
pdfToPDFA.outputFormat=Formato de salida
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Nivel de Marcador: Elige el nivel de marcadores para divi
splitByChapters.desc.3=Incluir Metadatos: Si está seleccionado, los metadatos del PDF original se incluirán en cada PDF dividido.
splitByChapters.desc.4=Permitir Duplicados: Si está seleccionado, permite que múltiples marcadores en la misma página creen archivos PDF separados.
splitByChapters.submit=Dividir PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Konpondu
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR modua
ocr.selectText.11=Irudiak ezabatu OCR-ren ondoren (Irudi GUZTIAK ezabatzen ditu, bakarrik da erabilgarri bihurketa urratsaren parte baldin bada)
ocr.selectText.12=Prozesaketa-mota (aurreratua)
ocr.help=Irakurri honen erabilerari buruzko dokumentazioa beste hizkuntza batzuetarako eta/edo ez erabili Docker-en
ocr.credit=Zerbitzu honek OCRmyPDF eta OCR-rako Tesseract erabiltzen ditu
ocr.credit=Zerbitzu honek qpdf eta OCR-rako Tesseract erabiltzen ditu
ocr.submit=PDF prozesatu OCR-rekin
@@ -887,7 +892,7 @@ fileToPDF.submit=PDF bihurtu
#compress
compress.title=Konprimatu
compress.header=PDFa konprimatu
compress.credit=Zerbitzu honek Ghostscript erabiltzen du PDFak komprimatzeko/optimizatzeko
compress.credit=Zerbitzu honek qpdf erabiltzen du PDFak komprimatzeko/optimizatzeko
compress.selectText.1=Eskuz 1etik 4ra
compress.selectText.2=Optimizazio maila:
compress.selectText.3=4 (Izugarria testu-irudietarako)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Aldatu
#pdfToPDFA
pdfToPDFA.title=PDFa PDF/A bihurtu
pdfToPDFA.header=PDFa PDF/A bihurtu
pdfToPDFA.credit=Zerbitzu honek ghostscript erabiltzen du PDFak PDF/A bihurtzeko
pdfToPDFA.credit=Zerbitzu honek qpdf erabiltzen du PDFak PDF/A bihurtzeko
pdfToPDFA.submit=Bihurtu
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -56,8 +56,8 @@ userNotFoundMessage=Utilisateur non trouvé.
incorrectPasswordMessage=Le mot de passe actuel est incorrect.
usernameExistsMessage=Le nouveau nom d'utilisateur existe déjà.
invalidUsernameMessage=Nom d'utilisateur invalide, 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.
invalidPasswordMessage=Le mot de passe ne peut pas être vide et ne doit pas contenir d'espaces au début ou en fin.
confirmPasswordErrorMessage=Nouveau Mot de passe et Confirmer le Nouveau Mot de passe doivent correspondre.
invalidPasswordMessage=Le mot de passe ne peut pas être vide et ne doit pas contenir d'espaces au début ou à la fin.
confirmPasswordErrorMessage=Le nouveau mot de passe et sa confirmation doivent être identiques.
deleteCurrentUserMessage=Impossible de supprimer l'utilisateur actuellement connecté.
deleteUsernameExistsMessage=Le nom d'utilisateur n'existe pas et ne peut pas être supprimé.
downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel.
@@ -81,7 +81,7 @@ page=Page
pages=Pages
loading=Chargement...
addToDoc=Ajouter au Document
reset=Reset
reset=Réinitialiser
legal.privacy=Politique de Confidentialité
legal.terms=Conditions Générales
@@ -142,7 +142,7 @@ navbar.language=Langues
navbar.settings=Paramètres
navbar.allTools=Outils
navbar.multiTool=Outils Multiples
navbar.search=Search
navbar.search=Rechercher
navbar.sections.organize=Organisation
navbar.sections.convertTo=Convertir en PDF
navbar.sections.convertFrom=Convertir depuis PDF
@@ -813,12 +813,17 @@ sign.draw=Dessiner une signature
sign.text=Saisir de texte
sign.clear=Effacer
sign.add=Ajouter
sign.saved=Saved Signatures
sign.saved=Sceaux enregistrées
sign.save=Enregistrer le sceau
sign.personalSigs=Sceaux personnels
sign.sharedSigs=Sceaux partagés
sign.noSavedSigs=Aucun sceau enregistré trouvé
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Réparer
@@ -863,7 +868,7 @@ ocr.selectText.10=Mode OCR
ocr.selectText.11=Supprimer les images après l'OCR (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'OCR pour d'autres langues ou une utilisation hors Docker :
ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l'OCR.
ocr.credit=Ce service utilise qpdf et Tesseract pour l'OCR.
ocr.submit=Traiter
@@ -887,7 +892,7 @@ fileToPDF.submit=Convertir
#compress
compress.title=Compresser un PDF
compress.header=Compresser un PDF (lorsque c'est possible!)
compress.credit=Ce service utilise Ghostscript pour la compression et l'optimisation des PDF.
compress.credit=Ce service utilise qpdf pour la compression et l'optimisation des PDF.
compress.selectText.1=Mode manuel de 1 à 4
compress.selectText.2=Niveau d'optimisation
compress.selectText.3=4 (terrible pour les images textuelles)
@@ -935,17 +940,29 @@ pdfOrganiser.placeholder=(par exemple 1,3,2 ou 4-8,2,10-12 ou 2n-1)
multiTool.title=Outil multifonction PDF
multiTool.header=Outil multifonction PDF
multiTool.uploadPrompts=Nom du fichier
multiTool.selectAll=Select All
multiTool.deselectAll=Deselect All
multiTool.selectPages=Page Select
multiTool.selectedPages=Selected Pages
multiTool.selectAll=Tout sélectionner
multiTool.deselectAll=Tout déselectionner
multiTool.selectPages=Sélection des pages
multiTool.selectedPages=Pages sélectionnées
multiTool.page=Page
multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.deleteSelected=Supprimer la sélection
multiTool.downloadAll=Exporter
multiTool.downloadSelected=Exporter la sélection
multiTool.insertPageBreak=Insérer un saut de page
multiTool.addFile=Ajouter un fichier
multiTool.rotateLeft=Rotation vers la gauche
multiTool.rotateRight=Rotation vers la droite
multiTool.split=Diviser
multiTool.moveLeft=Déplacer vers la gauche
multiTool.moveRight=Déplacer vers la droite
multiTool.delete=Supprimer
multiTool.dragDropMessage=Page(s) sélectionnées
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la <a href="{0}">page de l'outil multifonction</a>. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles !
#view pdf
viewPdf.title=Visualiser un PDF
@@ -1039,8 +1056,8 @@ watermark.selectText.1=PDF auquel ajouter un filigrane
watermark.selectText.2=Texte du filigrane
watermark.selectText.3=Taille de police
watermark.selectText.4=Rotation (de 0 à 360 degrés)
watermark.selectText.5=widthSpacer (espace entre chaque filigrane horizontalement)
watermark.selectText.6=heightSpacer (espace entre chaque filigrane verticalement)
watermark.selectText.5=Width Spacer (espace entre chaque filigrane horizontalement)
watermark.selectText.6=Height Spacer (espace entre chaque filigrane verticalement)
watermark.selectText.7=Opacité (de 0% à 100%)
watermark.selectText.8=Type de filigrane
watermark.selectText.9=Image du filigrane
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Modifier
#pdfToPDFA
pdfToPDFA.title=PDF en PDF/A
pdfToPDFA.header=PDF en PDF/A
pdfToPDFA.credit=Ce service utilise ghostscript pour la conversion en PDF/A.
pdfToPDFA.credit=Ce service utilise qpdf 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
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Niveau de Signet : Choisissez le niveau de signets à uti
splitByChapters.desc.3=Inclure les Métadonnées : Si coché, les métadonnées du PDF original seront incluses dans chaque PDF divisé.
splitByChapters.desc.4=Autoriser les Doublons : Si coché, permet à plusieurs signets sur la même page de créer des PDF séparés.
splitByChapters.submit=Diviser le PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Deisiúchán
@@ -863,7 +868,7 @@ ocr.selectText.10=Mód OCR
ocr.selectText.11=Bain íomhánna tar éis OCR (Bain GACH íomhá, ní úsáideach ach amháin má tá siad mar chuid den chéim tiontaithe)
ocr.selectText.12=Cineál Rindreála (Ardleibhéal)
ocr.help=Léigh le do thoil an doiciméadú seo ar conas é seo a úsáid do theangacha eile agus/nó úsáid nach bhfuil i ndugairí
ocr.credit=Úsáideann an tseirbhís seo OCRmyPDF agus Tesseract le haghaidh OCR.
ocr.credit=Úsáideann an tseirbhís seo qpdf agus Tesseract le haghaidh OCR.
ocr.submit=Próiseáil PDF le OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Tiontaigh go PDF
#compress
compress.title=Comhbhrúigh
compress.header=Comhbhrúigh PDF
compress.credit=Úsáideann an tseirbhís seo Ghostscript le haghaidh Comhbhrú/Optimization PDF.
compress.credit=Úsáideann an tseirbhís seo qpdf le haghaidh Comhbhrú/Optimization PDF.
compress.selectText.1=Mód Láimhe - Ó 1 go 4
compress.selectText.2=Leibhéal optamaithe:
compress.selectText.3=4 (Uafásach le haghaidh íomhánna téacs)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,7 +1056,7 @@ watermark.selectText.1=Roghnaigh PDF chun comhartha uisce a chur leis:
watermark.selectText.2=Téacs Comhartha Uisce:
watermark.selectText.3=Méid cló:
watermark.selectText.4=Rothlú (0-360):
watermark.selectText.5=widthSpacer (Spás idir gach comhartha uisce go cothrománach):
watermark.selectText.5=Width Spacer (Spás idir gach comhartha uisce go cothrománach):
watermark.selectText.6=spásaire airde (Spás idir gach comhartha uisce go hingearach):
watermark.selectText.7=Teimhneacht (0% - 100%):
watermark.selectText.8=Cineál Comhartha Uisce:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Athrú
#pdfToPDFA
pdfToPDFA.title=PDF Go PDF/A
pdfToPDFA.header=PDF Go PDF/A
pdfToPDFA.credit=Úsáideann an tseirbhís seo ghostscript chun PDF/A a thiontú
pdfToPDFA.credit=Úsáideann an tseirbhís seo qpdf chun PDF/A a thiontú
pdfToPDFA.submit=Tiontaigh
pdfToPDFA.tip=Faoi láthair ní oibríonn sé le haghaidh ionchuir iolracha ag an am céanna
pdfToPDFA.outputFormat=Formáid aschuir
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=प्रदर्शन बचाएं
sign.personalSigs=मौजूदा प्रदर्शन
sign.sharedSigs=साझेदार प्रदर्शन
sign.noSavedSigs=कोई भी संवर्तित प्रदर्शन नहीं मौजूद है
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=मरम्मत
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR मोड
ocr.selectText.11=OCR के बाद छवियां हटाएँ (सभी छवियां हटाएँ, केवल परिवर्तन चरण का हिस्सा होता है)
ocr.selectText.12=रेंडर टाइप (उन्नत)
ocr.help=कृपया इस डॉक्यूमेंटेशन को पढ़ें कि इसे अन्य भाषाओं के लिए कैसे उपयोग किया जाता है और/या डॉकर में नहीं हैं
ocr.credit=इस सेवा में OCRmyPDF और टेसरेक्ट का उपयोग होता है।
ocr.credit=इस सेवा में qpdf और टेसरेक्ट का उपयोग होता है।
ocr.submit=OCR के साथ PDF प्रोसेस करें
@@ -887,7 +892,7 @@ fileToPDF.submit=पीडीएफ़ में बदलें
#compress
compress.title=संकुचित करें
compress.header=PDF को संकुचित करें
compress.credit=यह सेवा PDF संकुचन/अनुकूलन के लिए Ghostscript का उपयोग करती है।
compress.credit=यह सेवा PDF संकुचन/अनुकूलन के लिए qpdf का उपयोग करती है।
compress.selectText.1=मैनुअल मोड - 1 से 4 तक
compress.selectText.2=अनुकूलन स्तर:
compress.selectText.3=4 (पाठ छवियों के लिए अत्यधिक)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=बदलें
#pdfToPDFA
pdfToPDFA.title=PDF से PDF/A में
pdfToPDFA.header=PDF से PDF/A में
pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए ghostscript का उपयोग किया जाता है।
pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए qpdf का उपयोग किया जाता है।
pdfToPDFA.submit=परिवर्तित करें
pdfToPDFA.tip=यह सैकड़ों प्रविष्टियाँ एक ही समय में काम करते हैं
pdfToPDFA.outputFormat=आउटपुट फॉर्मेट
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=लेमैक्स स्तर: विभाजन
splitByChapters.desc.3=मॉडेटरेट का शामिल करें: यदि सत्यापित किया जाता है, प्रारंभिक PDF की मॉडेटरेट को प्रत्येक विभाग PDF में शामिल किया जाएगा।
splitByChapters.desc.4=यादृच्छिक पुनरावृत्ति अनुमोदित: यदि सत्यापित किया जाता है, एक ही पेज पर दोहरे मूल्यांकन पब्लिक पीड़एफ बनाने की संभावना देता है।
splitByChapters.submit=PDF विभाजित
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Sačuvaj potpisnu oznaku
sign.personalSigs=Osobni potpisi
sign.sharedSigs=Dijeljeni potpisi
sign.noSavedSigs=Nema sacuvanih potpisa pronađenih
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Popravi
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR način
ocr.selectText.11=Ukloni slike nakon OCR-a (Uklanja SVE slike, korisno samo ako je dio koraka konverzije)
ocr.selectText.12=Vrsta iscrtavanja (napredno)
ocr.help=Pročitajte ovu dokumentaciju o tome kako ovo koristiti za druge jezike i/ili koristiti ne u dockeru
ocr.credit=Ova usluga koristi OCRmyPDF i Tesseract za OCR.
ocr.credit=Ova usluga koristi qpdf i Tesseract za OCR.
ocr.submit=Obradi PDF sa OCR-om
@@ -887,7 +892,7 @@ fileToPDF.submit=Pretvori u PDF
#compress
compress.title=Komprimirajte
compress.header=Komprimirajte PDF
compress.credit=Ova usluga koristi Ghostscript za komprimiranje / optimizaciju PDF-a.
compress.credit=Ova usluga koristi qpdf za komprimiranje / optimizaciju PDF-a.
compress.selectText.1=Ručni režim - Od 1 do 4
compress.selectText.2=Nivo optimizacije:
compress.selectText.3=4 (Užasno za tekstualne slike)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Promijeniti
#pdfToPDFA
pdfToPDFA.title=PDF u PDF/A
pdfToPDFA.header=PDF u PDF/A
pdfToPDFA.credit=Ova usluga koristi ghostscript za PDF/A pretvorbu
pdfToPDFA.credit=Ova usluga koristi qpdf za PDF/A pretvorbu
pdfToPDFA.submit=Pretvoriti
pdfToPDFA.tip=Trenutno ne radi za više unosa odjednom
pdfToPDFA.outputFormat=Izlazni format
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Nivo oznaka: Odaberite nivo oznaka koji će se koristiti
splitByChapters.desc.3=Uključi metapodatke: Ako je pokušano, metapodaci iz originalne PDF datoteke će biti uključeni u svaku podijeljenu PDF datoteku.
splitByChapters.desc.4=Dopuštaj duplikate: Ako je ova opcija zaštićena, dozvoljava se da se na istoj strani mogu stvoriti posebne PDF datoteke s više oznaka.
splitByChapters.submit=Podijeli PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Aláíráshoz mentés
sign.personalSigs=Személyi aláíráshoz
sign.sharedSigs=Megosztott aláíráshoz
sign.noSavedSigs=Nincsenek mentett aláírások találat
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Javítás
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR mód
ocr.selectText.11=Képek eltávolítása OCR után (Az ÖSSZES kép eltávolítása, csak akkor hasznos, ha a konverzió része)
ocr.selectText.12=Render típusa (Speciális)
ocr.help=Kérjük, olvassa el ezt a dokumentációt az egyéb nyelvek használatához és/vagy a nem Docker-es használathoz.
ocr.credit=Ez a szolgáltatás az OCRmyPDF és a Tesseract OCR használatával működik.
ocr.credit=Ez a szolgáltatás az qpdf és a Tesseract OCR használatával működik.
ocr.submit=PDF feldolgozása OCR-rel
@@ -887,7 +892,7 @@ fileToPDF.submit=Konvertálás PDF dokumentummá
#compress
compress.title=Tömörítés
compress.header=PDF tömörítése
compress.credit=Ez a szolgáltatás a Ghostscript-et használja a PDF tömörítéséhez/optimalizálásához.
compress.credit=Ez a szolgáltatás a qpdf-et használja a PDF tömörítéséhez/optimalizálásához.
compress.selectText.1=Kézi mód - 1-től 4-ig
compress.selectText.2=Optimalizálási szint:
compress.selectText.3=4 (nem ajánlott a szöveges képekhez)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Válassza ki a PDF-t, amelyhez vízjelet kíván hozzáad
watermark.selectText.2=Vízjel szövege:
watermark.selectText.3=Betűméret:
watermark.selectText.4=Forgatás (0-360):
watermark.selectText.5=widthSpacer (Hely a vízjelek között vízszintesen):
watermark.selectText.6=heightSpacer (Hely a vízjelek között függőlegesen):
watermark.selectText.5=Width Spacer (Hely a vízjelek között vízszintesen):
watermark.selectText.6=Height Spacer (Hely a vízjelek között függőlegesen):
watermark.selectText.7=Átlátszóság (0% - 100%):
watermark.selectText.8=Vízjel típusa:
watermark.selectText.9=Vízjel képe:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Módosítás
#pdfToPDFA
pdfToPDFA.title=PDF >> PDF/A
pdfToPDFA.header=PDF >> PDF/A
pdfToPDFA.credit=Ez a szolgáltatás az ghostscript-t használja a PDF/A konverzióhoz
pdfToPDFA.credit=Ez a szolgáltatás az qpdf-t használja a PDF/A konverzióhoz
pdfToPDFA.submit=Konvertálás
pdfToPDFA.tip=Jelenleg egyszerre több fájl nem működik ezzel a funkcióval
pdfToPDFA.outputFormat=Kimeneti formátum
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Metaadatok belefoglalása: Ha bevanítva van, az eredeti PDF fájl metaadatai megtartódnak minden osztott fájlban.
splitByChapters.desc.4=Duplikációk engedélyezése: Ha bevanítva van, lehetővé teszi a megadott oldalon lévő több kijelzőszint alapján új PDF-ek létrehozása.
splitByChapters.submit=PDF osztás
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Simpan Tanda Tangan
sign.personalSigs=Tanda Tangan Pribadi
sign.sharedSigs=Tanda Tangan Berbagi
sign.noSavedSigs=Tidak ditemukan tanda tangan yang disimpan
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Perbaiki
@@ -863,7 +868,7 @@ ocr.selectText.10=Mode OCR
ocr.selectText.11=Hapus gambar setelah OCR (Menghapus Semua gambar, hanya berguna jika merupakan bagian dari langkah konversi)
ocr.selectText.12=Jenis Render (Lanjutan)
ocr.help=Silakan baca dokumentasi ini tentang cara menggunakan ini untuk bahasa lain dan/atau penggunaan yang tidak ada di docker
ocr.credit=Layanan ini menggunakan OCRmyPDF dan Tesseract untuk OCR.
ocr.credit=Layanan ini menggunakan qpdf dan Tesseract untuk OCR.
ocr.submit=Memproses PDF dengan OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Konversi ke PDF
#compress
compress.title=Kompres
compress.header=Kompres PDF
compress.credit=Layanan ini menggunakan Ghostscript untuk Kompresi/Optimalisasi PDF.
compress.credit=Layanan ini menggunakan qpdf untuk Kompresi/Optimalisasi PDF.
compress.selectText.1=Mode Manual - Dari 1 hingga 4
compress.selectText.2=Tingkat Optimalisasi:
compress.selectText.3=4 (Buruk untuk gambar teks)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Pilih PDF untuk menambahkan watermark:
watermark.selectText.2=Text Watermark:
watermark.selectText.3=Ukuran Huruf:
watermark.selectText.4=Rotasi (0-360):
watermark.selectText.5=widthSpacer (Spasi diantara setiap watermark horisontal):
watermark.selectText.6=heightSpacer (Spasi diantara setiap watermark vertikal):
watermark.selectText.5=Width Spacer (Spasi diantara setiap watermark horisontal):
watermark.selectText.6=Height Spacer (Spasi diantara setiap watermark vertikal):
watermark.selectText.7=Kejernihan (0% - 100%):
watermark.selectText.8=Tipe Watermark:
watermark.selectText.9=Gambar Watermark:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Ganti
#pdfToPDFA
pdfToPDFA.title=PDF Ke PDF/A
pdfToPDFA.header=PDF ke PDF/A
pdfToPDFA.credit=Layanan ini menggunakan ghostscript untuk konversi PDF/A.
pdfToPDFA.credit=Layanan ini menggunakan qpdf untuk konversi PDF/A.
pdfToPDFA.submit=Konversi
pdfToPDFA.tip=Saat ini tidak dapat digunakan untuk beberapa input sekaligus
pdfToPDFA.outputFormat=Format keluaran
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Tingkatan Markah: Pilih tingkatan markah yang digunakan u
splitByChapters.desc.3=Termasuk Metadata: Jika dicentang, metadata asli PDF akan disertakan dalam setiap PDF yang dibagi.
splitByChapters.desc.4=Izinkan Duplikat: Jika dicentang, mengizinkan beberapa markah pada halaman yang sama untuk membuat PDF terpisah.
splitByChapters.submit=Pecah PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Firma salvata
sign.personalSigs=Firme personali
sign.sharedSigs=Firme condivise
sign.noSavedSigs=Nessuna firma salvata trovata
sign.addToAll=Aggiungi a tutte le pagine
sign.delete=Elimina
sign.first=Prima pagina
sign.last=Ultima pagina
sign.next=Prossima pagina
sign.previous=Pagina precedente
#repair
repair.title=Ripara
@@ -863,7 +868,7 @@ ocr.selectText.10=Modalità OCR
ocr.selectText.11=Rimuovi immagini dopo la scansione (Rimuove TUTTE le immagini, utile solo come parte del processo di conversione)
ocr.selectText.12=Modalità di rendering (avanzato)
ocr.help=Per favore leggi la documentazione su come usare il programma per altri linguaggi e/o uso non in Docker
ocr.credit=Questo servizio utilizza OCRmyPDF e Tesseract per l'OCR.
ocr.credit=Questo servizio utilizza qpdf e Tesseract per l'OCR.
ocr.submit=Scansiona testo nel PDF con OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Converti in PDF
#compress
compress.title=Comprimi
compress.header=Comprimi PDF
compress.credit=Questo servizio utilizza Ghostscript per la compressione/ottimizzazione dei PDF.
compress.credit=Questo servizio utilizza qpdf per la compressione/ottimizzazione dei PDF.
compress.selectText.1=Modalità manuale - Da 1 a 4
compress.selectText.2=Livello di ottimizzazione:
compress.selectText.3=4 (Terribile per le immagini di testo)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Elimina selezionata
multiTool.downloadAll=Esporta
multiTool.downloadSelected=Esporta selezionata
multiTool.insertPageBreak=Inserisci interruzione di pagina
multiTool.addFile=Aggiungi file
multiTool.rotateLeft=Ruota a sinistra
multiTool.rotateRight=Ruota a destra
multiTool.split=Dividi
multiTool.moveLeft=Sposta a sinistra
multiTool.moveRight=Sposta a destra
multiTool.delete=Elimina
multiTool.dragDropMessage=Pagina(e) selezionata(e)
multiTool.undo=Annulla
multiTool.redo=Rifai
#multiTool-advert
multiTool-advert.message=Questa funzione è disponibile anche nella nostra <a href="{0}">pagina multi-strumento</a>. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Cambia proprietà
#pdfToPDFA
pdfToPDFA.title=Da PDF a PDF/A
pdfToPDFA.header=Da PDF a PDF/A
pdfToPDFA.credit=Questo servizio utilizza Ghostscript per la conversione in PDF/A.
pdfToPDFA.credit=Questo servizio utilizza qpdf per la conversione in PDF/A.
pdfToPDFA.submit=Converti
pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente
pdfToPDFA.outputFormat=Formato di output
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Livello segnalibro: seleziona il livello dei segnalibri d
splitByChapters.desc.3=Includi metadati: se selezionato, i metadati del PDF originale verranno inclusi in ogni PDF diviso.
splitByChapters.desc.4=Consenti duplicati: se selezionata, consente più segnalibri sulla stessa pagina per creare PDF separati.
splitByChapters.submit=Dividi PDF
#File Chooser
fileChooser.click=Clicca
fileChooser.or=o
fileChooser.dragAndDrop=Trascina & Rilascia
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
#release notes
releases.footer=Rilasci
releases.title=Note di rilascio
releases.header=Note di rilascio
releases.current.version=Rilascio corrente
releases.note=Le note di rilascio sono disponibili solo in inglese

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=修復
@@ -863,7 +868,7 @@ ocr.selectText.10=OCRモード
ocr.selectText.11=OCR後に画像を削除する (すべての画像を削除します。変換ステップの一部である場合にのみ有効です)。
ocr.selectText.12=レンダリングタイプ (高度)
ocr.help=他の言語でこれを使用する方法やDocker以外で使用する方法についてはこのドキュメントをお読みください。
ocr.credit=本サービスにはOCRにOCRmyPDFとTesseractを使用しています。
ocr.credit=本サービスにはOCRにqpdfとTesseractを使用しています。
ocr.submit=OCRでPDFを処理する
@@ -887,7 +892,7 @@ fileToPDF.submit=PDFを変換
#compress
compress.title=圧縮
compress.header=PDFを圧縮
compress.credit=本サービスはPDFの圧縮/最適化にGhostscriptを使用しています。
compress.credit=本サービスはPDFの圧縮/最適化にqpdfを使用しています。
compress.selectText.1=手動モード - 1 から 4
compress.selectText.2=品質レベル:
compress.selectText.3=4 (テキスト画像は最悪)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=変更
#pdfToPDFA
pdfToPDFA.title=PDFをPDF/Aに変換
pdfToPDFA.header=PDFをPDF/Aに変換
pdfToPDFA.credit=本サービスはPDF/Aの変換にghostscriptを使用しています。
pdfToPDFA.credit=本サービスはPDF/Aの変換にqpdfを使用しています。
pdfToPDFA.submit=変換
pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません
pdfToPDFA.outputFormat=Output format
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=서명 저장
sign.personalSigs=개인용 서명
sign.sharedSigs=공유용 서명
sign.noSavedSigs=저장된 서명이 없습니다
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=복구
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR 모드
ocr.selectText.11=OCR 후 이미지 제거(모든 이미지 제거, 변환 단계의 일부인 경우에만 유용)
ocr.selectText.12=렌더 유형(고급)
ocr.help=다른 언어 또는 Docker에 포함되지 않은 언어에 대해 사용하는 방법에 대해서는 이 문서를 참조합니다.
ocr.credit=이 서비스는 OCR에 OCRmyPDF와 Tesseract를 사용합니다.
ocr.credit=이 서비스는 OCR에 qpdf와 Tesseract를 사용합니다.
ocr.submit=인식
@@ -887,7 +892,7 @@ fileToPDF.submit=PDF로 변환
#compress
compress.title=압축
compress.header=PDF 압축
compress.credit=이 서비스는 PDF 압축 및 최적화를 위해 Ghostscript를 사용합니다.
compress.credit=이 서비스는 PDF 압축 및 최적화를 위해 qpdf를 사용합니다.
compress.selectText.1=수동 모드 - 1에서 4
compress.selectText.2=최적화 수준:
compress.selectText.3=4 (텍스트 이미지에 적합하지 않음)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=변경
#pdfToPDFA
pdfToPDFA.title=PDF를 PDF/A로
pdfToPDFA.header=PDF 문서를 PDF/A로 변환
pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 ghostscript 문서를 사용합니다.
pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 qpdf 문서를 사용합니다.
pdfToPDFA.submit=변환
pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다.
pdfToPDFA.outputFormat=출력 형식
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=북마크 레벨: 분할에 사용할 북마크 레벨을
splitByChapters.desc.3=메타데이터 포함: 체크하면 각 분할된 PDF에는 원본 PDF의 메타데이터가 포함됩니다.
splitByChapters.desc.4=중복 허용: 중복 북마크가 있는 같은 페이지에 여러 번 분할 PDF를 생성합니다.
splitByChapters.submit=PDF 분할
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Opslaan Signatuur
sign.personalSigs=Persoonlijke Signatuuren
sign.sharedSigs=Gedeelde Signatuuren
sign.noSavedSigs=Geen opgeslagen signatuuren gevonden
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Repareren
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR-modus
ocr.selectText.11=Verwijder afbeeldingen na OCR (Verwijdert ALLE afbeeldingen, alleen nuttig als onderdeel van conversiestap)
ocr.selectText.12=Weergave Type (Geavanceerd)
ocr.help=Lees deze documentatie over hoe dit te gebruiken voor andere talen en/of gebruik buiten docker
ocr.credit=Deze dienst maakt gebruik van OCRmyPDF en Tesseract voor OCR.
ocr.credit=Deze dienst maakt gebruik van qpdf en Tesseract voor OCR.
ocr.submit=Verwerk PDF met OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Omzetten naar PDF
#compress
compress.title=Comprimeren
compress.header=PDF comprimeren
compress.credit=Deze functie gebruikt Ghostscript voor PDF Compressie/Optimalisatie.
compress.credit=Deze functie gebruikt qpdf voor PDF Compressie/Optimalisatie.
compress.selectText.1=Handmatige modus - Van 1 tot 4
compress.selectText.2=Optimalisatieniveau:
compress.selectText.3=4 (Verschrikkelijk voor tekstafbeeldingen)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Wijzigen
#pdfToPDFA
pdfToPDFA.title=PDF naar PDF/A
pdfToPDFA.header=PDF naar PDF/A
pdfToPDFA.credit=Deze service gebruikt ghostscript voor PDF/A-conversie
pdfToPDFA.credit=Deze service gebruikt qpdf voor PDF/A-conversie
pdfToPDFA.submit=Converteren
pdfToPDFA.tip=Werkt momenteel niet voor meerdere inputs tegelijkertijd.
pdfToPDFA.outputFormat=Uitvoerindeling
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Boekmarkeer niveau: Kies het boekmarkeer niveau om te geb
splitByChapters.desc.3=Metadata inclusief: Als gecijfeld, de originele PDF's metadata wordt ingevoegd in elk gesplitst PDF-bestand.
splitByChapters.desc.4=Dubbele items toestaan: Als gecijfeld, zorgen multiple boekmarkeersymboolen op dezelfde pagina voor het maken van aparte PDF-bestanden.
splitByChapters.submit=PDF splitsen
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparer
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR-modus
ocr.selectText.11=Fjern bilder etter OCR (Fjerner ALLE bilder, kun nyttig hvis det er en del av konverteringsprosessen)
ocr.selectText.12=Renderingstype (Avansert)
ocr.help=Vennligst les denne dokumentasjonen for hvordan du bruker dette for andre språk og/eller bruk utenfor Docker.
ocr.credit=Denne tjenesten bruker OCRmyPDF og Tesseract for OCR.
ocr.credit=Denne tjenesten bruker qpdf og Tesseract for OCR.
ocr.submit=Behandle PDF med OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Konverter til PDF
#compress
compress.title=Komprimer
compress.header=Komprimer PDF
compress.credit=Denne tjenesten bruker Ghostscript for PDF-komprimering/optimisering.
compress.credit=Denne tjenesten bruker qpdf for PDF-komprimering/optimisering.
compress.selectText.1=Manuell modus - Fra 1 til 4
compress.selectText.2=Optimeringsnivå:
compress.selectText.3=4 (Dårlig for tekstbilder)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Endre
#pdfToPDFA
pdfToPDFA.title=PDF til PDF/A
pdfToPDFA.header=PDF til PDF/A
pdfToPDFA.credit=Denne tjenesten bruker ghostscript for PDF/A-konvertering
pdfToPDFA.credit=Denne tjenesten bruker qpdf for PDF/A-konvertering
pdfToPDFA.submit=Konverter
pdfToPDFA.tip=Fungere for øyeblikket ikke for flere innganger samtidig
pdfToPDFA.outputFormat=Utdataformat
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Napraw
@@ -863,7 +868,7 @@ ocr.selectText.10=Tryb OCR
ocr.selectText.11=Usuń obrazy po OCR (usuwa wszystkie obrazy, przydatne tylko, jeśli jest częścią etapu konwersji)
ocr.selectText.12=Typ renderowania (zaawansowany)
ocr.help=Przeczytaj tę dokumentację, aby dowiedzieć się, jak używać tego w innych językach i/lub nie używać docker
ocr.credit=Ta usługa używa OCRmyPDF i Tesseract do OCR.
ocr.credit=Ta usługa używa qpdf i Tesseract do OCR.
ocr.submit=Przetwarzaj PDF za pomocą OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Konwertuj na PDF
#compress
compress.title=Kompresuj
compress.header=Kompresuj PDF
compress.credit=Ta usługa używa Ghostscript do kompresji/optymalizacji PDF.
compress.credit=Ta usługa używa qpdf do kompresji/optymalizacji PDF.
compress.selectText.1=Tryb ręczny - Od 1 do 4
compress.selectText.2=Poziom optymalizacji:
compress.selectText.3=4 (Duże dla obrazów tekstowych)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Zmień
#pdfToPDFA
pdfToPDFA.title=PDF na PDF/A
pdfToPDFA.header=PDF na PDF/A
pdfToPDFA.credit=Ta usługa używa ghostscript do konwersji PDF/A
pdfToPDFA.credit=Ta usługa używa qpdf do konwersji PDF/A
pdfToPDFA.submit=Konwertuj
pdfToPDFA.tip=Tylko jeden plik na raz
pdfToPDFA.outputFormat=Format wyjściowy:
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Poziom Zakładek: Wybierz poziom zakładek, który ma zos
splitByChapters.desc.3=Dołącz Metadane: Jeśli opcja ta jest zaznaczona, metadane oryginalnego pliku PDF zostaną uwzględnione w każdym rozdzielonych plików PDF.
splitByChapters.desc.4=Zezwól na Duplikaty: Jeśli ta opcja jest zaznaczona, pozwala na tworzenie oddzielnych plików PDF przez wiele zakładek na tej samej stronie.
splitByChapters.submit=Podziel PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Salvar Assinatura
sign.personalSigs=Assinaturas Pessoais
sign.sharedSigs=Assinaturas Compartilhadas
sign.noSavedSigs=Nenhuma assinatura salva encontrada
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparar
@@ -863,7 +868,7 @@ ocr.selectText.10=Modo OCR
ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão)
ocr.selectText.12=Tipo de Renderização (avançado)
ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker
ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR.
ocr.credit=Este serviço usa qpdf e Tesseract para OCR.
ocr.submit=Processar PDF com OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Converter para PDF
#compress
compress.title=Comprimir
compress.header=Comprimir PDF
compress.credit=Este serviço usa o Ghostscript para compressão/otimização de PDF.
compress.credit=Este serviço usa o qpdf para compressão/otimização de PDF.
compress.selectText.1=Modo Manual - De 1 a 4
compress.selectText.2=Nível de Otimização:
compress.selectText.3=4 (Pior para imagens de texto)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Selecione PDF para adicionar a marca d'água:
watermark.selectText.2=Texto da marca d'água:
watermark.selectText.3=Tamanho da fonte:
watermark.selectText.4=Rotação (0-360):
watermark.selectText.5=widthSpacer (Espaço entre cada marca d'água horizontalmente):
watermark.selectText.6=heightSpacer (Espaço entre cada marca d'água verticalmente):
watermark.selectText.5=Width Spacer (Espaço entre cada marca d'água horizontalmente):
watermark.selectText.6=Height Spacer (Espaço entre cada marca d'água verticalmente):
watermark.selectText.7=Opacidade (0% - 100%):
watermark.selectText.8=Tipo de marca d'água:
watermark.selectText.9=Imagem da marca d'água:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Alterar
#pdfToPDFA
pdfToPDFA.title=PDF para PDF/A
pdfToPDFA.header=PDF para PDF/A
pdfToPDFA.credit=Este serviço usa ghostscript para conversão de PDF/A
pdfToPDFA.credit=Este serviço usa qpdf para conversão de PDF/A
pdfToPDFA.submit=Converter
pdfToPDFA.tip=Atualmente não funciona para múltiplas entradas ao mesmo tempo
pdfToPDFA.outputFormat=Formato de saída
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Nível de Marcador: Escolha o nível de marcador a ser us
splitByChapters.desc.3=Incluir Metadados: Se marcado, os metadados do PDF original serão incluidos em cada divisão do PDF.
splitByChapters.desc.4=Permitir Cópias: Se marcado, habilita vários marcadores na mesma página para criar PDFs separados.
splitByChapters.submit=Dividir PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Guardar Assinatura
sign.personalSigs=Assinaturas Pessoais
sign.sharedSigs=Assinaturas Compartilhadas
sign.noSavedSigs=Nenhuma assinatura guardada encontrada
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparar
@@ -863,7 +868,7 @@ ocr.selectText.10=Modo OCR
ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão)
ocr.selectText.12=Tipo de renderização (avançado)
ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker
ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR.
ocr.credit=Este serviço usa qpdf e Tesseract para OCR.
ocr.submit=Processar PDF com OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Converter para PDF
#compress
compress.title=Comprimir
compress.header=Comprimir PDF
compress.credit=Este serviço usa o Ghostscript para compressão/otimização de PDF.
compress.credit=Este serviço usa o qpdf para compressão/otimização de PDF.
compress.selectText.1=Modo Manual - De 1 a 4
compress.selectText.2=Nível de Otimização:
compress.selectText.3=4 (Pior para imagens de texto)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Seleccione o PDF para Adicionar a Marca d'Água
watermark.selectText.2=Texto da Marca d'Água
watermark.selectText.3=Tamanho da Fonte
watermark.selectText.4=Rotação (0-360)
watermark.selectText.5=Espaçamento Horizontal (widthSpacer)
watermark.selectText.6=Espaçamento Vertical (heightSpacer)
watermark.selectText.5=Espaçamento Horizontal (Width Spacer)
watermark.selectText.6=Espaçamento Vertical (Height Spacer)
watermark.selectText.7=Opacidade (0% - 100%)
watermark.selectText.8=Tipo de Marca d'Água
watermark.selectText.9=Imagem da Marca d'Água
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Mudar
#pdfToPDFA
pdfToPDFA.title=PDF para PDF/A
pdfToPDFA.header=PDF para PDF/A
pdfToPDFA.credit=Este serviço usa ghostscript para Conversão de PDF/A
pdfToPDFA.credit=Este serviço usa qpdf para Conversão de PDF/A
pdfToPDFA.submit=Converter
pdfToPDFA.tip=Actualmente não funciona para múltiplos inputs de uma só vez
pdfToPDFA.outputFormat=Formato de saída
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Nível de Anotações: Escolha o nível das anotações a
splitByChapters.desc.3=Inclua Metadados: Se marcado, os metadados originais do PDF serão incluídos em cada PDF dividido.
splitByChapters.desc.4=Permitir Duplicatas: Se marcado, permite a criação de vários bookmarks na mesma página para criar separadamente vários PDFs.
splitByChapters.submit=Dividir o PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Repară
@@ -863,7 +868,7 @@ ocr.selectText.10=Mod OCR
ocr.selectText.11=Elimină imaginile după OCR (Elimină TOATE imaginile, util doar în etapa de conversie)
ocr.selectText.12=Tip de redare (Avansat)
ocr.help=Citiți documentația pentru a afla cum să utilizați acest serviciu pentru alte limbi și/sau în afara mediului Docker
ocr.credit=Acest serviciu utilizează OCRmyPDF și Tesseract pentru OCR.
ocr.credit=Acest serviciu utilizează qpdf și Tesseract pentru OCR.
ocr.submit=Procesează PDF-ul cu OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Convertiți în PDF
#compress
compress.title=Comprimare
compress.header=Comprimare PDF
compress.credit=Acest serviciu utilizează OCRmyPDF pentru comprimarea/optimizarea PDF-urilor.
compress.credit=Acest serviciu utilizează qpdf pentru comprimarea/optimizarea PDF-urilor.
compress.selectText.1=Nivel de optimizare:
compress.selectText.2=0 (Fără optimizare)
compress.selectText.3=1 (Implicit, optimizare fără pierdere)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Schimbă
#pdfToPDFA
pdfToPDFA.title=PDF către PDF/A
pdfToPDFA.header=PDF către PDF/A
pdfToPDFA.credit=Acest serviciu utilizează ghostscript pentru conversia în PDF/A
pdfToPDFA.credit=Acest serviciu utilizează qpdf pentru conversia în PDF/A
pdfToPDFA.submit=Convertește
pdfToPDFA.tip=În prezent nu funcționează pentru mai multe intrări simultan
pdfToPDFA.outputFormat=Format de ieșire
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Сохранить подпись
sign.personalSigs=Личные подписи
sign.sharedSigs=Общие подписи
sign.noSavedSigs=Найдено ни одной сохраненной подписи
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Ремонт
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR режим
ocr.selectText.11=Удалить изображения после OCR (удаляет ВСЕ изображения, полезно только в том случае, если они являются частью шага преобразования)
ocr.selectText.12=Тип рендера (расширенный)
ocr.help=Прочтите эту документацию о том, как использовать это для других языков и/или использовать не в докере.
ocr.credit=Этот сервис использует OCRmyPDF и Tesseract для OCR.
ocr.credit=Этот сервис использует qpdf и Tesseract для OCR.
ocr.submit=Обработка PDF с OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Преобразовать в PDF
#compress
compress.title=Сжать
compress.header=Сжать PDF
compress.credit=Эта служба использует Ghostscript для сжатия/оптимизации PDF.
compress.credit=Эта служба использует qpdf для сжатия/оптимизации PDF.
compress.selectText.1=Ручной режим - от 1 до 4
compress.selectText.2=Уровень оптимизации:
compress.selectText.3=4 (Ужасно для текстовых изображений)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Выберите PDF, чтобы добавить вод
watermark.selectText.2=Текст водяного знака:
watermark.selectText.3=Размер шрифта:
watermark.selectText.4=Поворот (0-360):
watermark.selectText.5=widthSpacer (пробел между каждым водяным знаком по горизонтали):
watermark.selectText.6=heightSpacer (пробел между каждым водяным знаком по вертикали):
watermark.selectText.5=Width Spacer (пробел между каждым водяным знаком по горизонтали):
watermark.selectText.6=Height Spacer (пробел между каждым водяным знаком по вертикали):
watermark.selectText.7=Непрозрачность (0% - 100%):
watermark.selectText.8=Тип водяного знака:
watermark.selectText.9=Изображение водяного знака:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Изменить
#pdfToPDFA
pdfToPDFA.title=PDF в PDF/A
pdfToPDFA.header=PDF в PDF/A
pdfToPDFA.credit=Этот сервис использует ghostscript для преобразования PDF/A
pdfToPDFA.credit=Этот сервис использует qpdf для преобразования PDF/A
pdfToPDFA.submit=Конвертировать
pdfToPDFA.tip=В настоящее время не поддерживается при нескольких входных данных одновременно
pdfToPDFA.outputFormat=Формат вывода
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Уровень закладки: выберите уро
splitByChapters.desc.3=Включить метаданные: если эта опция отмечена, метаданные исходного PDF будут включены в каждый разбитый PDF.
splitByChapters.desc.4=Позволять дубликаты: если эта опция отмечена, на одной странице могут быть созданы несколько PDF из-за нескольких одинаковых закладок.
splitByChapters.submit=Разделить PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Opraviť
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR režim
ocr.selectText.11=Odstrániť obrázky po OCR (Odstráni VŠETKY obrázky, užitočné iba ak je súčasťou konverzného kroku)
ocr.selectText.12=Typ vykreslenia (Pokročilé)
ocr.help=Prosím, prečítajte si túto dokumentáciu o tom, ako používať OCR pre iné jazyky a/alebo použitie mimo docker
ocr.credit=Táto služba používa OCRmyPDF a Tesseract pre OCR.
ocr.credit=Táto služba používa qpdf a Tesseract pre OCR.
ocr.submit=Spracovať PDF s OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Konvertovať do PDF
#compress
compress.title=Komprimovať
compress.header=Komprimovať PDF
compress.credit=Táto služba používa Ghostscript pre kompresiu/optimalizáciu PDF.
compress.credit=Táto služba používa qpdf pre kompresiu/optimalizáciu PDF.
compress.selectText.1=Manuálny režim - Od 1 do 4
compress.selectText.2=Úroveň optimalizácie:
compress.selectText.3=4 (Hrozné pre textové obrázky)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Zmeniť
#pdfToPDFA
pdfToPDFA.title=PDF na PDF/A
pdfToPDFA.header=PDF na PDF/A
pdfToPDFA.credit=Táto služba používa ghostscript na konverziu PDF/A
pdfToPDFA.credit=Táto služba používa qpdf na konverziu PDF/A
pdfToPDFA.submit=Konvertovať
pdfToPDFA.tip=Momentálne nefunguje pre viacero vstupov naraz
pdfToPDFA.outputFormat=Výstupný formát
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Popravi
@@ -863,7 +868,7 @@ ocr.selectText.10=Režim OCR-a
ocr.selectText.11=Ukloni slike nakon OCR-a (Uklanja SVE slike, korisno samo ako je deo koraka konverzije)
ocr.selectText.12=Tip rendiranja (Napredno)
ocr.help=Molimo vas da pročitate ovu dokumentaciju o tome kako koristiti ovo za druge jezike i/ili korišćenje van docker-a
ocr.credit=Ova usluga koristi OCRmyPDF i Tesseract za OCR.
ocr.credit=Ova usluga koristi qpdf i Tesseract za OCR.
ocr.submit=Obradi PDF sa OCR-om
@@ -887,7 +892,7 @@ fileToPDF.submit=Konvertuj u PDF
#compress
compress.title=Kompresija
compress.header=Kompresuj PDF
compress.credit=Ova usluga koristi Ghostscript za kompresiju / optimizaciju PDF-a.
compress.credit=Ova usluga koristi qpdf za kompresiju / optimizaciju PDF-a.
compress.selectText.1=Ručni režim - Od 1 do 4
compress.selectText.2=Nivo optimizacije:
compress.selectText.3=4 (Užasno za tekstualne slike)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Promeni
#pdfToPDFA
pdfToPDFA.title=PDF u PDF/A
pdfToPDFA.header=PDF u PDF/A
pdfToPDFA.credit=Ova usluga koristi ghostscript za konverziju u PDF/A format
pdfToPDFA.credit=Ova usluga koristi qpdf za konverziju u PDF/A format
pdfToPDFA.submit=Konvertuj
pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Spara signatur
sign.personalSigs=Personliga signaturer
sign.sharedSigs=Delade signaturer
sign.noSavedSigs=Inga sparade signaturer hittades
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Reparera
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR-läge
ocr.selectText.11=Ta bort bilder efter OCR (tar bort ALLA bilder, endast användbart som en del av konverteringssteget)
ocr.selectText.12=Renderingstyp (avancerat)
ocr.help=Vänligen läs denna dokumentation om hur du använder detta för andra språk och/eller använder inte i docker
ocr.credit=Denna tjänst använder OCRmyPDF och Tesseract för OCR.
ocr.credit=Denna tjänst använder qpdf och Tesseract för OCR.
ocr.submit=Bearbeta PDF med OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Konvertera till PDF
#compress
compress.title=Komprimera
compress.header=Komprimera PDF
compress.credit=Denna tjänst använder Ghostscript för PDF-komprimering/optimering.
compress.credit=Denna tjänst använder qpdf för PDF-komprimering/optimering.
compress.selectText.1=Manuellt läge - Från 1 till 4
compress.selectText.2=Optimeringsnivå:
compress.selectText.3=4 (Fruktansvärt för textbilder)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Välj PDF för att lägga till vattenstämpel till:
watermark.selectText.2=Vattenmärkestext:
watermark.selectText.3=Teckenstorlek:
watermark.selectText.4=Vändning (0-360):
watermark.selectText.5=widthSpacer (mellanrum mellan varje vattenstämpel horisontellt):
watermark.selectText.6=heightSpacer (mellanrum mellan varje vattenstämpel vertikalt):
watermark.selectText.5=Width Spacer (mellanrum mellan varje vattenstämpel horisontellt):
watermark.selectText.6=Height Spacer (mellanrum mellan varje vattenstämpel vertikalt):
watermark.selectText.7=Opacitet (0% - 100%):
watermark.selectText.8=Vattenstämpeltyp:
watermark.selectText.9=Vattenstämpelbild:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Ändra
#pdfToPDFA
pdfToPDFA.title=PDF till PDF/A
pdfToPDFA.header=PDF till PDF/A
pdfToPDFA.credit=Denna tjänst använder ghostscript för PDF/A-konvertering
pdfToPDFA.credit=Denna tjänst använder qpdf för PDF/A-konvertering
pdfToPDFA.submit=Konvertera
pdfToPDFA.tip=Fungerar för närvarande inte för flera inmatningar samtidigt
pdfToPDFA.outputFormat=Utdataformat
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bokmärkesnivå: Välj nivån av bokmärken att använda
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Tillåt duplicieringar: Om kryssrutan är markerad tillåts flera bokmärken på samma sida skapa individuella PDF:er.
splitByChapters.submit=Dela upp PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=บันทึกลายเซ็น
sign.personalSigs=ลายเซ็นส่วนตัว
sign.sharedSigs=ลายเซ็นร่วม
sign.noSavedSigs=ไม่พบลายเซ็นที่บันทึกไว้
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=ซ่อมแซม
@@ -863,7 +868,7 @@ ocr.selectText.10=โหมด OCR
ocr.selectText.11=ลบภาพหลังจาก OCR (ลบภาพทั้งหมด, มีประโยชน์เฉพาะหากเป็นส่วนหนึ่งของขั้นตอนการแปลง)
ocr.selectText.12=ประเภทการเรนเดอร์ (ขั้นสูง)
ocr.help=โปรดอ่านเอกสารนี้เพื่อใช้งานภาษาอื่นๆ และ/หรือใช้งานนอก docker
ocr.credit=บริการนี้ใช้ OCRmyPDF และ Tesseract สำหรับ OCR
ocr.credit=บริการนี้ใช้ qpdf และ Tesseract สำหรับ OCR
ocr.submit=ประมวลผล PDF ด้วย OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=แปลงเป็น PDF
#compress
compress.title=บีบอัด
compress.header=บีบอัด PDF
compress.credit=บริการนี้ใช้ Ghostscript สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF
compress.credit=บริการนี้ใช้ qpdf สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF
compress.selectText.1=โหมดแมนนวล - จาก 1 ถึง 4
compress.selectText.2=ระดับการเพิ่มประสิทธิภาพ:
compress.selectText.3=4 (ไม่ดีสำหรับภาพข้อความ)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=เปลี่ยน
#pdfToPDFA
pdfToPDFA.title=PDF เป็น PDF/A
pdfToPDFA.header=PDF เป็น PDF/A
pdfToPDFA.credit=บริการนี้ใช้ ghostscript สำหรับการแปลง PDF/A
pdfToPDFA.credit=บริการนี้ใช้ qpdf สำหรับการแปลง PDF/A
pdfToPDFA.submit=แปลง
pdfToPDFA.tip=ปัจจุบันไม่ทำงานสำหรับการป้อนข้อมูลหลายรายการพร้อมกัน
pdfToPDFA.outputFormat=รูปแบบผลลัพธ์
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=ระดับบุคคลที่ได้รับ
splitByChapters.desc.3=รวมข้อมูลเสริม: หากถูกเลือก ข้อมูลเสริมของไฟล์ PDF ที่เดิมจะถูกรวมอยู่ในแต่ละไฟล์ที่แบ่งออก
splitByChapters.desc.4=อนุญาตให้มีการซ้ำ: หากถูกเลือก จะทำให้สามารถสร้างไฟล์ PDF แยกออกมาจากหน้าเดียวกันได้หลายรายการ
splitByChapters.submit=แบ่งไฟล์ PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Onar
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR Modu
ocr.selectText.11=OCR'den sonra resimleri kaldır (TÜM resimleri kaldırır, sadece dönüşüm adımının bir parçasıysa yararlıdır)
ocr.selectText.12=Render Türü (İleri Seviye)
ocr.help=Lütfen bu belgede başka dillerde nasıl kullanılacağı ve/veya docker'da kullanılmaması hakkında bilgi edinin
ocr.credit=Bu hizmet OCR için OCRmyPDF ve Tesseract'ı kullanır.
ocr.credit=Bu hizmet OCR için qpdf ve Tesseract'ı kullanır.
ocr.submit=PDF'i OCR(Metin Tanıma) ile İşle
@@ -887,7 +892,7 @@ fileToPDF.submit=PDF'e Dönüştür
#compress
compress.title=Sıkıştır
compress.header=PDF'i Sıkıştır
compress.credit=Bu hizmet PDF Sıkıştırma/Optimizasyonu için Ghostscript kullanır.
compress.credit=Bu hizmet PDF Sıkıştırma/Optimizasyonu için qpdf kullanır.
compress.selectText.1=Manuel Mod - 1'den 4'e
compress.selectText.2=Optimizasyon seviyesi:
compress.selectText.3=4 (Metin resimleri için hiç uygun değil)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Değiştir
#pdfToPDFA
pdfToPDFA.title=PDF'den PDF/A'ya
pdfToPDFA.header=PDF'den PDF/A'ya
pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için ghostscript kullanır
pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için qpdf kullanır
pdfToPDFA.submit=Dönüştür
pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor
pdfToPDFA.outputFormat=Çıkış formatı
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Ремонт
@@ -863,7 +868,7 @@ ocr.selectText.10=Режим OCR
ocr.selectText.11=Видалити зображення після OCR (видаляє ВСІ зображення, корисно лише в тому випадку, якщо вони є частиною етапу перетворення)
ocr.selectText.12=Тип рендеру (розширений)
ocr.help=Прочитайте цю документацію про те, як використовувати це для інших мов і/або використовувати не в докері.
ocr.credit=Цей сервіс використовує OCRmyPDF та Tesseract для OCR.
ocr.credit=Цей сервіс використовує qpdf та Tesseract для OCR.
ocr.submit=Обробка PDF з OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Перетворити у PDF
#compress
compress.title=Стиснути
compress.header=Стиснути PDF
compress.credit=Ця служба використовує Ghostscript для стиснення/оптимізації PDF.
compress.credit=Ця служба використовує qpdf для стиснення/оптимізації PDF.
compress.selectText.1=Ручний режим - від 1 до 4
compress.selectText.2=Рівень оптимізації:
compress.selectText.3=4 (Жахливо для текстових зображень)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=Виберіть PDF, щоб додати водяний
watermark.selectText.2=Текст водяного знаку:
watermark.selectText.3=Розмір шрифту:
watermark.selectText.4=Обертання (0-360):
watermark.selectText.5=widthSpacer (проміжок між кожним водяним знаком по горизонталі):
watermark.selectText.6=heightSpacer (проміжок між кожним водяним знаком по вертикалі):
watermark.selectText.5=Width Spacer (проміжок між кожним водяним знаком по горизонталі):
watermark.selectText.6=Height Spacer (проміжок між кожним водяним знаком по вертикалі):
watermark.selectText.7=Непрозорість (0% - 100%):
watermark.selectText.8=Тип водяного знаку:
watermark.selectText.9=Зображення водяного знаку:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Змінити
#pdfToPDFA
pdfToPDFA.title=PDF в PDF/A
pdfToPDFA.header=PDF в PDF/A
pdfToPDFA.credit=Цей сервіс використовує ghostscript для перетворення у формат PDF/A
pdfToPDFA.credit=Цей сервіс використовує qpdf для перетворення у формат PDF/A
pdfToPDFA.submit=Конвертувати
pdfToPDFA.tip=Наразі не працює для кількох вхідних файлів одночасно
pdfToPDFA.outputFormat=Вихідний формат
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=Sửa chữa
@@ -863,7 +868,7 @@ ocr.selectText.10=Chế độ OCR
ocr.selectText.11=Xóa hình ảnh sau khi OCR (Xóa TẤT CẢ hình ảnh, chỉ hữu ích nếu là một phần của bước chuyển đổi)
ocr.selectText.12=Loại hiển thị (Nâng cao)
ocr.help=Vui lòng đọc tài liệu này về cách sử dụng cho các ngôn ngữ khác và/hoặc sử dụng không trong docker
ocr.credit=Dịch vụ này sử dụng OCRmyPDF và Tesseract cho OCR.
ocr.credit=Dịch vụ này sử dụng qpdf và Tesseract cho OCR.
ocr.submit=Xử lý PDF với OCR
@@ -887,7 +892,7 @@ fileToPDF.submit=Chuyển đổi sang PDF
#compress
compress.title=Nén
compress.header=Nén PDF
compress.credit=Dịch vụ này sử dụng Ghostscript để Nén/Tối ưu hóa PDF.
compress.credit=Dịch vụ này sử dụng qpdf để Nén/Tối ưu hóa PDF.
compress.selectText.1=Chế độ thủ công - Từ 1 đến 4
compress.selectText.2=Mức độ tối ưu hóa:
compress.selectText.3=4 (Tệ cho hình ảnh văn bản)
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=Thay đổi
#pdfToPDFA
pdfToPDFA.title=PDF sang PDF/A
pdfToPDFA.header=PDF sang PDF/A
pdfToPDFA.credit=Dịch vụ này sử dụng ghostscript để chuyển đổi PDF/A
pdfToPDFA.credit=Dịch vụ này sử dụng qpdf để chuyển đổi PDF/A
pdfToPDFA.submit=Chuyển đổi
pdfToPDFA.tip=Hiện tại không hoạt động với nhiều đầu vào cùng lúc
pdfToPDFA.outputFormat=Định dạng đầu ra
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=Save Signature
sign.personalSigs=Personal Signatures
sign.sharedSigs=Shared Signatures
sign.noSavedSigs=No saved signatures found
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=修复
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR模式
ocr.selectText.11=OCR后移除图像移除所有图像只有在转换步骤中才有用
ocr.selectText.12=渲染类型(高级)
ocr.help=请阅读此文档,了解如何将其用于其他语言和/或不在docker中使用。
ocr.credit=此服务使用OCRmyPDF和Tesseract进行OCR。
ocr.credit=此服务使用qpdf和Tesseract进行OCR。
ocr.submit=用OCR处理PDF
@@ -887,7 +892,7 @@ fileToPDF.submit=转换为 PDF
#compress
compress.title=压缩
compress.header=压缩PDF
compress.credit=此服务使用Ghostscript进行PDF压缩/优化。
compress.credit=此服务使用qpdf进行PDF压缩/优化。
compress.selectText.1=手动模式 - 从 1 到 4
compress.selectText.2=优化级别:
compress.selectText.3=4文本图像很糟糕
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1097,7 +1114,7 @@ changeMetadata.submit=更改
#pdfToPDFA
pdfToPDFA.title=PDF转PDF/A
pdfToPDFA.header=将PDF转换为PDF/A
pdfToPDFA.credit=此服务使用ghostscript进行PDF/A转换
pdfToPDFA.credit=此服务使用qpdf进行PDF/A转换
pdfToPDFA.submit=转换
pdfToPDFA.tip=目前不支持上传多个
pdfToPDFA.outputFormat=输出格式
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -818,7 +818,12 @@ sign.save=儲存簽章
sign.personalSigs=個人簽章
sign.sharedSigs=共用簽章
sign.noSavedSigs=尚未儲存任何簽章
sign.addToAll=Add to all pages
sign.delete=Delete
sign.first=First page
sign.last=Last page
sign.next=Next page
sign.previous=Previous page
#repair
repair.title=修復
@@ -863,7 +868,7 @@ ocr.selectText.10=OCR 模式
ocr.selectText.11=移除 OCR 後的影像(移除所有影像,只有在轉換步驟中才有用)
ocr.selectText.12=渲染類型(進階)
ocr.help=請閱讀此文件,了解如何使用其他語言和/或在 Docker 中使用
ocr.credit=此服務使用 OCRmyPDF 和 Tesseract 進行 OCR。
ocr.credit=此服務使用 qpdf 和 Tesseract 進行 OCR。
ocr.submit=使用 OCR 處理 PDF
@@ -887,7 +892,7 @@ fileToPDF.submit=轉換為 PDF
#compress
compress.title=壓縮
compress.header=壓縮 PDF
compress.credit=此服務使用 Ghostscript 進行 PDF 壓縮/最佳化。
compress.credit=此服務使用 qpdf 進行 PDF 壓縮/最佳化。
compress.selectText.1=手動模式 - 從 1 到 4
compress.selectText.2=最佳化等級:
compress.selectText.3=4對於含有文字的影像來說結果很糟
@@ -944,6 +949,18 @@ multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=Export
multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Rotate Right
multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo
multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1039,8 +1056,8 @@ watermark.selectText.1=選擇要新增浮水印的 PDF
watermark.selectText.2=浮水印文字:
watermark.selectText.3=字型大小:
watermark.selectText.4=旋轉0-360
watermark.selectText.5=widthSpacer每個浮水印之間的水平間距
watermark.selectText.6=heightSpacer每個浮水印之間的垂直間距
watermark.selectText.5=Width Spacer每個浮水印之間的水平間距
watermark.selectText.6=Height Spacer每個浮水印之間的垂直間距
watermark.selectText.7=不透明度0% - 100%
watermark.selectText.8=浮水印類型:
watermark.selectText.9=浮水印影像:
@@ -1097,7 +1114,7 @@ changeMetadata.submit=變更
#pdfToPDFA
pdfToPDFA.title=PDF 轉 PDF/A
pdfToPDFA.header=PDF 轉 PDF/A
pdfToPDFA.credit=此服務使用 ghostscript 進行 PDF/A 轉換
pdfToPDFA.credit=此服務使用 qpdf 進行 PDF/A 轉換
pdfToPDFA.submit=轉換
pdfToPDFA.tip=目前不支援上傳多個
pdfToPDFA.outputFormat=輸出格式
@@ -1245,3 +1262,16 @@ splitByChapters.desc.2=書籤層級選擇用於分割的書籤層級0 表
splitByChapters.desc.3=包含中繼資料:如果勾選,原始 PDF 的中繼資料將包含在每個分割後的 PDF 中。
splitByChapters.desc.4=允許重複:如果勾選,允許同一頁面上的多個書籤建立獨立的 PDF。
splitByChapters.submit=分割 PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes
releases.footer=Releases
releases.title=Release Notes
releases.header=Release Notes
releases.current.version=Current Release
releases.note=Release notes are only available in English

View File

@@ -16,7 +16,7 @@ security:
csrfDisabled: true # set to 'true' to disable CSRF protection (not recommended for production)
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2)
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
initialLogin:
username: '' # initial username for the first login
password: '' # initial password for the first login
@@ -42,14 +42,14 @@ security:
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
clientId: '' # client ID from your provider
clientSecret: '' # client secret from your provider
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
useAsUsername: email # default is 'email'; custom fields can be used as the username
scopes: openid, profile, email # specify the scopes for which the application will request permissions
provider: google # set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
saml2:
enabled: false # currently in alpha, not recommended for use yet, enableAlphaFunctionality must be set to true
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
enabled: false # Only enabled for paid enterprise clients (enterpriseEdition.enabled must be true)
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
registrationId: stirling
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata
@@ -107,9 +107,9 @@ processExecutor:
sessionLimit: # Process executor instances limits
libreOfficeSessionLimit: 1
pdfToHtmlSessionLimit: 1
ocrMyPdfSessionLimit: 2
qpdfSessionLimit: 4
tesseractSessionLimit: 1
pythonOpenCvSessionLimit: 8
ghostScriptSessionLimit: 16
weasyPrintSessionLimit: 16
installAppSessionLimit: 1
calibreSessionLimit: 1
@@ -117,7 +117,7 @@ processExecutor:
libreOfficetimeoutMinutes: 30
pdfToHtmltimeoutMinutes: 20
pythonOpenCvtimeoutMinutes: 30
ghostScripttimeoutMinutes: 30
weasyPrinttimeoutMinutes: 30
installApptimeoutMinutes: 60
calibretimeoutMinutes: 30
tesseractTimeoutMinutes: 30

View File

@@ -3,14 +3,14 @@
{
"moduleName": "ch.qos.logback:logback-classic",
"moduleUrl": "http://www.qos.ch",
"moduleVersion": "1.5.11",
"moduleVersion": "1.5.12",
"moduleLicense": "GNU Lesser General Public License",
"moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
},
{
"moduleName": "ch.qos.logback:logback-core",
"moduleUrl": "http://www.qos.ch",
"moduleVersion": "1.5.11",
"moduleVersion": "1.5.12",
"moduleLicense": "GNU Lesser General Public License",
"moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
},
@@ -45,77 +45,77 @@
{
"moduleName": "com.fasterxml.jackson.core:jackson-annotations",
"moduleUrl": "https://github.com/FasterXML/jackson",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.core:jackson-core",
"moduleUrl": "https://github.com/FasterXML/jackson-core",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.core:jackson-databind",
"moduleUrl": "https://github.com/FasterXML/jackson",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml",
"moduleUrl": "https://github.com/FasterXML/jackson-dataformats-text",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8",
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310",
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base",
"moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-base",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider",
"moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-json-provider",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.module:jackson-module-jaxb-annotations",
"moduleUrl": "https://github.com/FasterXML/jackson-modules-base",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names",
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.fasterxml.jackson:jackson-bom",
"moduleUrl": "https://github.com/FasterXML/jackson-bom",
"moduleVersion": "2.17.2",
"moduleVersion": "2.18.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -146,6 +146,18 @@
"moduleLicense": "GNU General Public License v3.0",
"moduleLicenseUrl": "https://api.github.com/licenses/gpl-3.0"
},
{
"moduleName": "com.github.jai-imageio:jai-imageio-core",
"moduleUrl": "https://github.com/jai-imageio/jai-imageio-core",
"moduleVersion": "1.4.0",
"moduleLicense": "LICENSE.txt"
},
{
"moduleName": "com.github.jai-imageio:jai-imageio-jpeg2000",
"moduleUrl": "https://github.com/jai-imageio/jai-imageio-jpeg2000",
"moduleVersion": "1.4.0",
"moduleLicense": "LICENSE-JJ2000.txt, LICENSE-Sun.txt"
},
{
"moduleName": "com.github.stephenc.jcip:jcip-annotations",
"moduleUrl": "http://stephenc.github.com/jcip-annotations",
@@ -162,21 +174,22 @@
},
{
"moduleName": "com.google.errorprone:error_prone_annotations",
"moduleVersion": "2.11.0",
"moduleUrl": "https://errorprone.info/error_prone_annotations",
"moduleVersion": "2.28.0",
"moduleLicense": "Apache 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.google.guava:failureaccess",
"moduleUrl": "https://github.com/google/guava/",
"moduleVersion": "1.0.1",
"moduleVersion": "1.0.2",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "com.google.guava:guava",
"moduleUrl": "https://github.com/google/guava/",
"moduleVersion": "31.1-jre",
"moduleVersion": "33.3.1-jre",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -189,8 +202,8 @@
{
"moduleName": "com.google.j2objc:j2objc-annotations",
"moduleUrl": "https://github.com/google/j2objc/",
"moduleVersion": "1.3",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleVersion": "3.0.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
@@ -222,7 +235,7 @@
{
"moduleName": "com.h2database:h2",
"moduleUrl": "https://h2database.com",
"moduleVersion": "2.1.214",
"moduleVersion": "2.3.232",
"moduleLicense": "MPL 2.0",
"moduleLicenseUrl": "https://www.mozilla.org/en-US/MPL/2.0/"
},
@@ -370,10 +383,17 @@
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "commons-cli:commons-cli",
"moduleUrl": "http://commons.apache.org/proper/commons-cli/",
"moduleVersion": "1.4",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "commons-codec:commons-codec",
"moduleUrl": "https://commons.apache.org/proper/commons-codec/",
"moduleVersion": "1.16.1",
"moduleVersion": "1.17.1",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -387,16 +407,16 @@
{
"moduleName": "commons-io:commons-io",
"moduleUrl": "https://commons.apache.org/proper/commons-io/",
"moduleVersion": "2.17.0",
"moduleVersion": "2.18.0",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "commons-logging:commons-logging",
"moduleUrl": "http://jakarta.apache.org/commons/logging/",
"moduleVersion": "1.0.4",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "/LICENSE.txt"
"moduleUrl": "https://commons.apache.org/proper/commons-logging/",
"moduleVersion": "1.3.3",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "io.dropwizard.metrics:metrics-core",
@@ -414,34 +434,34 @@
{
"moduleName": "io.micrometer:micrometer-commons",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.13.6",
"moduleVersion": "1.14.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "io.micrometer:micrometer-core",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.13.6",
"moduleVersion": "1.14.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "io.micrometer:micrometer-jakarta9",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.13.6",
"moduleVersion": "1.14.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "io.micrometer:micrometer-observation",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.13.6",
"moduleVersion": "1.14.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "io.smallrye:jandex",
"moduleVersion": "3.1.2",
"moduleVersion": "3.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -573,7 +593,7 @@
},
{
"moduleName": "net.bytebuddy:byte-buddy",
"moduleVersion": "1.14.19",
"moduleVersion": "1.15.10",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -611,10 +631,17 @@
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.apache.commons:commons-csv",
"moduleUrl": "https://commons.apache.org/proper/commons-csv/",
"moduleVersion": "1.9.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.apache.commons:commons-lang3",
"moduleUrl": "https://commons.apache.org/proper/commons-lang/",
"moduleVersion": "3.14.0",
"moduleVersion": "3.17.0",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -641,13 +668,13 @@
},
{
"moduleName": "org.apache.logging.log4j:log4j-api",
"moduleVersion": "2.23.1",
"moduleVersion": "2.24.1",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.apache.logging.log4j:log4j-to-slf4j",
"moduleVersion": "2.23.1",
"moduleVersion": "2.24.1",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -695,7 +722,7 @@
{
"moduleName": "org.apache.tomcat.embed:tomcat-embed-el",
"moduleUrl": "https://tomcat.apache.org/",
"moduleVersion": "10.1.31",
"moduleVersion": "10.1.33",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -733,31 +760,52 @@
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.bouncycastle:bcmail-jdk15on",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.69",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
{
"moduleName": "org.bouncycastle:bcpkix-jdk15on",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.69",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
{
"moduleName": "org.bouncycastle:bcpkix-jdk18on",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.78.1",
"moduleVersion": "1.79",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
{
"moduleName": "org.bouncycastle:bcprov-jdk18on",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.78.1",
"moduleVersion": "1.79",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
{
"moduleName": "org.bouncycastle:bcutil-jdk15on",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.69",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
{
"moduleName": "org.bouncycastle:bcutil-jdk18on",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.78.1",
"moduleVersion": "1.79",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
{
"moduleName": "org.checkerframework:checker-qual",
"moduleUrl": "https://checkerframework.org",
"moduleVersion": "3.12.0",
"moduleUrl": "https://checkerframework.org/",
"moduleVersion": "3.43.0",
"moduleLicense": "The MIT License",
"moduleLicenseUrl": "http://opensource.org/licenses/MIT"
},
@@ -790,182 +838,182 @@
{
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-client",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-common",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-servlet",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-annotations",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-plus",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlet",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlets",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-webapp",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-client",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-common",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-server",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-api",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-common",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-alpn-client",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-client",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-ee",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-http",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-io",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-plus",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-security",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-server",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-session",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-util",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
{
"moduleName": "org.eclipse.jetty:jetty-xml",
"moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.14",
"moduleVersion": "12.0.15",
"moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
},
@@ -1000,23 +1048,23 @@
{
"moduleName": "org.hibernate.common:hibernate-commons-annotations",
"moduleUrl": "http://hibernate.org",
"moduleVersion": "6.0.6.Final",
"moduleLicense": "GNU Library General Public License v2.1 or later",
"moduleLicenseUrl": "http://www.opensource.org/licenses/LGPL-2.1"
"moduleVersion": "7.0.3.Final",
"moduleLicense": "Apache License Version 2.0",
"moduleLicenseUrl": "https://opensource.org/licenses/Apache-2.0"
},
{
"moduleName": "org.hibernate.orm:hibernate-core",
"moduleUrl": "https://www.hibernate.org/orm/6.5",
"moduleVersion": "6.5.3.Final",
"moduleUrl": "https://www.hibernate.org/orm/6.6",
"moduleVersion": "6.6.2.Final",
"moduleLicense": "GNU Library General Public License v2.1 or later",
"moduleLicenseUrl": "https://www.opensource.org/licenses/LGPL-2.1"
},
{
"moduleName": "org.jboss.logging:jboss-logging",
"moduleUrl": "http://www.jboss.org",
"moduleVersion": "3.5.3.Final",
"moduleLicense": "Public Domain",
"moduleLicenseUrl": "http://repository.jboss.org/licenses/cc0-1.0.txt"
"moduleVersion": "3.6.1.Final",
"moduleLicense": "Apache License 2.0",
"moduleLicenseUrl": "https://repository.jboss.org/licenses/apache-2.0.txt"
},
{
"moduleName": "org.latencyutils:LatencyUtils",
@@ -1025,6 +1073,12 @@
"moduleLicense": "Public Domain, per Creative Commons CC0",
"moduleLicenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/"
},
{
"moduleName": "org.locationtech.jts:jts-core",
"moduleVersion": "1.18.1",
"moduleLicense": "Eclipse Public License, Version 2.0",
"moduleLicenseUrl": "https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt"
},
{
"moduleName": "org.opensaml:opensaml-core",
"moduleVersion": "4.3.2",
@@ -1100,21 +1154,21 @@
{
"moduleName": "org.ow2.asm:asm",
"moduleUrl": "http://asm.ow2.org",
"moduleVersion": "9.7",
"moduleVersion": "9.7.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.ow2.asm:asm-commons",
"moduleUrl": "http://asm.ow2.org",
"moduleVersion": "9.7",
"moduleVersion": "9.7.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.ow2.asm:asm-tree",
"moduleUrl": "http://asm.ow2.org",
"moduleVersion": "9.7",
"moduleVersion": "9.7.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -1153,273 +1207,273 @@
{
"moduleName": "org.springframework.boot:spring-boot",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-actuator",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-actuator-autoconfigure",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-autoconfigure",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-devtools",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-actuator",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-aop",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-data-jpa",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-jdbc",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-jetty",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-json",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-logging",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-oauth2-client",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-security",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-thymeleaf",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-web",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.data:spring-data-commons",
"moduleUrl": "https://spring.io/projects/spring-data",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.data:spring-data-jpa",
"moduleUrl": "https://projects.spring.io/spring-data-jpa",
"moduleVersion": "3.3.5",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-config",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-core",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-crypto",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-oauth2-client",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-oauth2-core",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-oauth2-jose",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-saml2-service-provider",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.security:spring-security-web",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.3.4",
"moduleVersion": "6.4.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.session:spring-session-core",
"moduleUrl": "https://spring.io/projects/spring-session",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-aop",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-aspects",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-beans",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-context",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-core",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-expression",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-jcl",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-jdbc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-orm",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-tx",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-web",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-webmvc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.14",
"moduleVersion": "6.2.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
@@ -1464,10 +1518,17 @@
{
"moduleName": "org.yaml:snakeyaml",
"moduleUrl": "https://bitbucket.org/snakeyaml/snakeyaml",
"moduleVersion": "2.2",
"moduleVersion": "2.3",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "technology.tabula:tabula",
"moduleUrl": "http://github.com/tabulapdf/tabula-java",
"moduleVersion": "1.0.5",
"moduleLicense": "MIT License",
"moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php"
},
{
"moduleName": "xml-apis:xml-apis",
"moduleUrl": "http://xml.apache.org/commons/components/external/",

View File

@@ -19,6 +19,15 @@
transform-origin: top left;
}
#drag-container .multidrag {
position: fixed;
max-width: 200px;
max-height: 200px;
transform-origin: top left;
margin-left: 1rem;
background-color: rgba(0, 29, 41, 0.9);
}
.drag-manager_dragging {
width: 0px;
visibility: hidden;

View File

@@ -1,10 +1,221 @@
.custom-file-chooser {
display: flex;
flex-direction: column;
position: relative;
min-height: 55px;
border-radius: 1rem;
--selected-files-display: none;
}
.input-container {
position: relative;
border-radius: 1rem;
border: 1px dashed rgb(105, 116, 134);
column-gap: 7px;
row-gap: 7px;
height: 150px;
width: 100%;
--overlay-display: none;
transition: background-color 0.5s linear;
}
.input-container:hover {
outline: none;
border: none;
background-color: var(--md-sys-color-surface-container-low);
-webkit-transition: box-shadow 1s ease, background-color 2s linear;
-moz-transition: box-shadow 1s ease, background-color 2s linear;
-o-transition: box-shadow 1s ease, background-color 2s linear;
-ms-transition: box-shadow 1s ease, background-color 2s linear;
transition: box-shadow 1s ease, background-color 2s linear;
box-shadow: 0 0 10px rgb(105, 116, 134);
cursor: pointer;
}
.input-container * {
user-select: none;
pointer-events: none;
}
.input-container::before {
display: var(--overlay-display);
position: absolute;
content: '';
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: var(--md-sys-color-surface);
z-index: 1;
white-space: pre;
border-radius: 1rem;
}
.input-container::after {
display: var(--overlay-display);
position: absolute;
content: attr(data-text);
font-size: 0.9rem;
font-weight: 550;
color: var(--md-sys-color-on-surface);
background-color: transparent;
min-width: 150px;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
text-align: center;
z-index: 2;
}
input[type="file"] {
display: none;
}
.input-container div:nth-of-type(2) {
color: var(--md-sys-color-on-surface);
}
.input-container div:nth-of-type(1), .input-container div:nth-of-type(3) {
color: var(--md-sys-color-on-surface);
font-size: 16px;
font-weight: bold;
}
.file-input-btn {
display: inline-block;
border: 1px solid #ccc;
padding: 6px 12px;
cursor: pointer;
color: #212529;
font-size: 1rem;
border-radius: 3rem;
background-color: #DDE0E3;
}
.small-file-container {
padding-top: 1px;
position: relative;
row-gap: 1px;
height: 60px;
width: 60px;
}
.file-icon {
display: flex;
align-items: center;
justify-content: center;
height: 30px;
width: 30px;
}
.file-icon * {
height: inherit;
width: inherit;
}
.file-info {
min-width: 0;
}
.file-info > div:nth-child(1) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--md-sys-color-on-surface);
max-width: 60px;
font-size: 0.75rem;
}
.file-info > div:nth-child(2) {
overflow: hidden;
text-overflow: ellipsis;
color: grey;
max-width: 60px;
font-size: 10px;
}
.remove-selected-file {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
height: 15px;
width: 15px;
right: 10px;
top: -5px;
}
.remove-selected-file * {
overflow: hidden;
height: inherit;
width: inherit;
z-index: 3;
pointer-events: none;
user-select: none;
}
.remove-selected-file:after {
content: '';
position: absolute;
left: 1;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: white;
z-index: 2;
user-select: none;
pointer-events: none;
}
.remove-selected-file:hover {
cursor: pointer;
}
.custom-file-label {
padding-right: 90px;
}
.selected-files {
margin-top: 10px;
max-height: 150px;
overflow-y: auto;
display: var(--selected-files-display);
padding-left: 5px;
padding-right: 3px;
padding-top: 15px;
padding-bottom: 15px;
flex: 1;
white-space: pre-wrap;
row-gap: 12px;
column-gap: 5px;
border-radius: 1rem;
border: 1px solid rgb(105, 116, 134, 0.5);
}

View File

@@ -100,3 +100,30 @@ input:-webkit-autofill:focus {
input[data-autocompleted] {
background-color: transparent !important;
}
.btn-tooltip {
position: absolute;
display: none;
bottom: 3.2rem;
white-space: nowrap;
flex-wrap: nowrap;
width: fit-content;
padding: 7px;
background-color: rgba(0, 29, 41, 0.9);
border-radius: 3px;
font-size: 12px;
color: whitesmoke;
animation: fadeup 0.15s linear;
}
@keyframes fadeup {
0% {
transform: translateY(10px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.btn:hover .btn-tooltip {
display: block;
}

View File

@@ -61,10 +61,6 @@ label {
padding: 0;
}
#export-button {
margin-left: auto;
}
.bg-card {
background-color: var(--md-sys-color-surface-5);
border-radius: 3rem;
@@ -290,3 +286,6 @@ label {
.checkbox-label {
font-size: medium;
}
.btn {
position: relative;
}

View File

@@ -1,5 +1,5 @@
.pdf-actions_button-container {
z-index: 2;
z-index: 4;
display: flex;
opacity: 0;
transition: opacity 0.1s linear;
@@ -46,7 +46,7 @@
width: 80px;
height: 100%;
z-index: 1;
z-index: 3;
opacity: 0;
transition: opacity 0.2s;
}
@@ -116,6 +116,7 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
translate: 50% -50%;
aspect-ratio: 1;
border-radius: 100px;
z-index: 4;
}
.pdf-actions_split-file-button {
@@ -125,9 +126,9 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
translate: 0 -50%;
aspect-ratio: 1;
border-radius: 100px;
z-index: 3;
}
.pdf-actions_checkbox {
position: absolute;
top: 5px;
@@ -137,7 +138,7 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
padding: 6px 8px;
border-radius: 8px;
font-size: 16px;
z-index: 2;
z-index: 10;
}
.hidden {
@@ -150,4 +151,5 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
translate: 0% -50%;
aspect-ratio: 1;
border-radius: 100px;
z-index: 5;
}

View File

@@ -62,53 +62,54 @@ select#font-select option {
background-color: rgba(52, 152, 219, 0.2);
/* Darken background on hover */
}
.signature-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
padding: 1rem;
max-height: 400px;
overflow-y: auto;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
padding: 1rem;
max-height: 400px;
overflow-y: auto;
}
.signature-list {
max-height: 400px;
overflow-y: auto;
max-height: 400px;
overflow-y: auto;
}
.signature-list-item {
padding: 0.75rem;
border: 1px solid #dee2e6;
border-radius: 4px;
margin-bottom: 0.5rem;
cursor: pointer;
transition: background-color 0.2s;
padding: 0.75rem;
border: 1px solid #dee2e6;
border-radius: 4px;
margin-bottom: 0.5rem;
cursor: pointer;
transition: background-color 0.2s;
}
.signature-list-item:hover {
background-color: #f8f9fa;
background-color: #f8f9fa;
}
.signature-list-info {
display: flex;
justify-content: space-between;
align-items: center;
display: flex;
justify-content: space-between;
align-items: center;
}
.signature-list-name {
font-weight: 500;
font-weight: 500;
}
.signature-list-details {
color: #6c757d;
font-size: 0.875rem;
color: #6c757d;
font-size: 0.875rem;
}
.signature-list-details small:not(:last-child) {
margin-right: 1rem;
margin-right: 1rem;
}
.view-toggle {
text-align: right;
padding: 0.5rem 1rem;
}
text-align: right;
padding: 0.5rem 1rem;
}

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-az" viewBox="0 0 640 480">
<path fill="#3f9c35" d="M.1 0h640v480H.1z"/>
<path fill="#ed2939" d="M.1 0h640v320H.1z"/>
<path fill="#00b9e4" d="M.1 0h640v160H.1z"/>
<circle cx="304" cy="240" r="72" fill="#fff"/>
<circle cx="320" cy="240" r="60" fill="#ed2939"/>
<path fill="#fff" d="m384 200 7.7 21.5 20.6-9.8-9.8 20.7L424 240l-21.5 7.7 9.8 20.6-20.6-9.8L384 280l-7.7-21.5-20.6 9.8 9.8-20.6L344 240l21.5-7.7-9.8-20.6 20.6 9.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@@ -34,6 +34,14 @@
const url = this.action;
const files = $("#fileInput-input")[0].files;
const formData = new FormData(this);
const submitButton = document.getElementById("submitBtn");
const showGameBtn = document.getElementById("show-game-btn");
const originalButtonText = submitButton.textContent;
var boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
if (showGameBtn) {
showGameBtn.style.display = "none";
}
// Remove empty file entries
for (let [key, value] of formData.entries()) {
@@ -42,14 +50,10 @@
}
}
const override = $("#override").val() || "";
const originalButtonText = $("#submitBtn").text();
$("#submitBtn").text("Processing...");
console.log(override);
// Set a timeout to show the game button if operation takes more than 5 seconds
const timeoutId = setTimeout(() => {
var boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
const showGameBtn = document.getElementById("show-game-btn");
if (boredWaiting === "enabled" && showGameBtn) {
showGameBtn.style.display = "block";
showGameBtn.parentNode.insertBefore(document.createElement('br'), showGameBtn.nextSibling);
@@ -57,6 +61,9 @@
}, 5000);
try {
submitButton.textContent = "Processing...";
submitButton.disabled = true;
if (remoteCall === true) {
if (override === "multi" || (!multipleInputsForSingleRequest && files.length > 1 && override !== "single")) {
await submitMultiPdfForm(url, files);
@@ -65,12 +72,17 @@
}
}
clearFileInput();
clearTimeout(timeoutId);
$("#submitBtn").text(originalButtonText);
if (showGameBtn) {
showGameBtn.style.display = "none";
showGameBtn.style.marginTop = "";
}
submitButton.textContent = originalButtonText;
submitButton.disabled = false;
// After process finishes, check for boredWaiting and gameDialog open status
const boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
const gameDialog = document.getElementById('game-container-wrapper');
if (boredWaiting === "enabled" && gameDialog && gameDialog.open) {
// Display a green banner at the bottom of the screen saying "Download complete"
@@ -87,23 +99,41 @@
}
} catch (error) {
clearFileInput();
clearTimeout(timeoutId);
showGameBtn.style.display = "none";
submitButton.textContent = originalButtonText;
submitButton.disabled = false;
handleDownloadError(error);
$("#submitBtn").text(originalButtonText);
console.error(error);
}
});
});
async function getPDFPageCount(file) {
try {
const arrayBuffer = await file.arrayBuffer();
pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs-legacy/pdf.worker.mjs'
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
return pdf.numPages;
} catch (error) {
console.error('Error getting PDF page count:', error);
return null;
}
}
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
const startTime = performance.now();
const file = formData.get('fileInput');
let success = false;
let errorMessage = null;
try {
const response = await fetch(url, { method: "POST", body: formData });
const contentType = response.headers.get("content-type");
if (!response.ok) {
errorMessage = response.status;
if (response.status === 401) {
// Handle 401 Unauthorized error
showSessionExpiredPrompt();
return;
}
@@ -118,6 +148,8 @@
let filename = getFilenameFromContentDisposition(contentDisposition);
const blob = await response.blob();
success = true;
if (contentType.includes("application/pdf") || contentType.includes("image/")) {
clearFileInput();
return handleResponse(blob, filename, !isMulti, isZip);
@@ -127,13 +159,29 @@
}
} catch (error) {
clearFileInput();
success = false;
errorMessage = error.message;
console.error("Error in handleSingleDownload:", error);
throw error;
} finally {
const processingTime = performance.now() - startTime;
// Capture analytics
const pageCount = file && file.type === 'application/pdf' ? await getPDFPageCount(file) : null;
if(analyticsEnabled) {
posthog.capture('file_processing', {
success: success,
file_type: file ? file.type || 'unknown' : 'unknown',
file_size: file ? file.size : 0,
processing_time: processingTime,
error_message: errorMessage,
pdf_pages: pageCount
});
}
}
}
function getFilenameFromContentDisposition(contentDisposition) {
function getFilenameFromContentDisposition(contentDisposition) {
let filename;
if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) {

View File

@@ -4,6 +4,7 @@ const DraggableUtils = {
nextId: 0,
pdfDoc: null,
pageIndex: 0,
elementAllPages: [],
documentsMap: new Map(),
lastInteracted: null,
@@ -197,6 +198,68 @@ const DraggableUtils = {
deleteAllDraggableCanvases() {
this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach((el) => el.remove());
},
async addAllPagesDraggableCanvas(element) {
if (element) {
let currentPage = this.pageIndex
if (!this.elementAllPages.includes(element)) {
this.elementAllPages.push(element)
element.style.filter = 'sepia(1) hue-rotate(90deg) brightness(1.2)';
let newElement = {
"element": element,
"offsetWidth": element.width,
"offsetHeight": element.height
}
let pagesMap = this.documentsMap.get(this.pdfDoc);
if (!pagesMap) {
pagesMap = {};
this.documentsMap.set(this.pdfDoc, pagesMap);
}
let page = this.pageIndex
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
if (pagesMap[`${pageIndex}-offsetWidth`]) {
if (!pagesMap[pageIndex].includes(newElement)) {
pagesMap[pageIndex].push(newElement);
}
} else {
pagesMap[pageIndex] = []
pagesMap[pageIndex].push(newElement)
pagesMap[`${pageIndex}-offsetWidth`] = pagesMap[`${page}-offsetWidth`];
pagesMap[`${pageIndex}-offsetHeight`] = pagesMap[`${page}-offsetHeight`];
}
await this.goToPage(pageIndex)
}
} else {
const index = this.elementAllPages.indexOf(element);
if (index !== -1) {
this.elementAllPages.splice(index, 1);
}
element.style.filter = '';
let pagesMap = this.documentsMap.get(this.pdfDoc);
if (!pagesMap) {
pagesMap = {};
this.documentsMap.set(this.pdfDoc, pagesMap);
}
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
if (pagesMap[`${pageIndex}-offsetWidth`] && pageIndex != currentPage) {
const pageElements = pagesMap[pageIndex];
pageElements.forEach(elementPage => {
const elementIndex = pageElements.findIndex(elementPage => elementPage['element'].id === element.id);
if (elementIndex !== -1) {
pageElements.splice(elementIndex, 1);
}
});
}
await this.goToPage(pageIndex)
}
}
await this.goToPage(currentPage)
}
},
deleteDraggableCanvas(element) {
if (element) {
//Check if deleted element is the last interacted
@@ -241,7 +304,7 @@ const DraggableUtils = {
}
const draggablesData = pagesMap[this.pageIndex];
if (draggablesData) {
if (draggablesData && Array.isArray(draggablesData)) {
draggablesData.forEach((draggableData) => this.boxDragContainer.appendChild(draggableData.element));
}
@@ -273,6 +336,13 @@ const DraggableUtils = {
//return pdfCanvas.toDataURL();
},
async goToPage(pageIndex) {
this.storePageContents();
await this.renderPage(this.pdfDoc, pageIndex);
this.loadPageContents();
},
async incrementPage() {
if (this.pageIndex < this.pdfDoc.numPages - 1) {
this.storePageContents();
@@ -297,6 +367,7 @@ const DraggableUtils = {
this.storePageContents();
const pagesMap = this.documentsMap.get(this.pdfDoc);
for (let pageIdx in pagesMap) {
if (pageIdx.includes("offset")) {
continue;
@@ -304,7 +375,8 @@ const DraggableUtils = {
console.log(typeof pageIdx);
const page = pdfDocModified.getPage(parseInt(pageIdx));
const draggablesData = pagesMap[pageIdx];
let draggablesData = pagesMap[pageIdx];
const offsetWidth = pagesMap[pageIdx + "-offsetWidth"];
const offsetHeight = pagesMap[pageIdx + "-offsetHeight"];
@@ -383,7 +455,6 @@ const DraggableUtils = {
});
}
}
this.loadPageContents();
return pdfDocModified;
},

View File

@@ -0,0 +1,52 @@
class FileIconFactory {
static createFileIcon(fileExtension) {
let ext = fileExtension.toLowerCase();
switch (ext) {
case "pdf":
return this.createPDFIcon();
case "csv":
return this.createCSVIcon();
case "jpe":
case "jpg":
case "jpeg":
case "gif":
case "png":
case "bmp":
case "ico":
case "svg":
case "svgz":
case "tif":
case "tiff":
case "ai":
case "drw":
case "pct":
case "psp":
case "xcf":
case "psd":
case "raw":
case "webp":
case "heic":
return this.createImageIcon();
default:
return this.createUnknownFileIcon();
}
}
static createPDFIcon() {
return `
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-filetype-pdf" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM1.6 11.85H0v3.999h.791v-1.342h.803q.43 0 .732-.173.305-.175.463-.474a1.4 1.4 0 0 0 .161-.677q0-.375-.158-.677a1.2 1.2 0 0 0-.46-.477q-.3-.18-.732-.179m.545 1.333a.8.8 0 0 1-.085.38.57.57 0 0 1-.238.241.8.8 0 0 1-.375.082H.788V12.48h.66q.327 0 .512.181.185.183.185.522m1.217-1.333v3.999h1.46q.602 0 .998-.237a1.45 1.45 0 0 0 .595-.689q.196-.45.196-1.084 0-.63-.196-1.075a1.43 1.43 0 0 0-.589-.68q-.396-.234-1.005-.234zm.791.645h.563q.371 0 .609.152a.9.9 0 0 1 .354.454q.118.302.118.753a2.3 2.3 0 0 1-.068.592 1.1 1.1 0 0 1-.196.422.8.8 0 0 1-.334.252 1.3 1.3 0 0 1-.483.082h-.563zm3.743 1.763v1.591h-.79V11.85h2.548v.653H7.896v1.117h1.606v.638z"/>
</svg>
`;
}
static createImageIcon() {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M216-144q-30 0-51-21.5T144-216v-528q0-29 21-50.5t51-21.5h528q30 0 51 21.5t21 50.5v528q0 29-21 50.5T744-144H216Zm48-144h432L552-480 444-336l-72-96-108 144Z"/></svg>`;
}
static createUnknownFileIcon() {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M263.72-96Q234-96 213-117.15T192-168v-624q0-29.7 21.15-50.85Q234.3-864 264-864h312l192 192v504q0 29.7-21.16 50.85Q725.68-96 695.96-96H263.72ZM528-624h168L528-792v168Z"/></svg>`;
}
}
export default FileIconFactory;

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