Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7eb7774979 | ||
|
|
10158fa408 | ||
|
|
dab6613f1b | ||
|
|
1d016df92e | ||
|
|
0631e3071c | ||
|
|
ba1b4b502c | ||
|
|
7865bf720f | ||
|
|
06abc82fbc | ||
|
|
3afacf2405 | ||
|
|
ba259a2d8d | ||
|
|
45895cd6bf | ||
|
|
66d0ad5071 | ||
|
|
c5ae576541 | ||
|
|
2bcdd8cce6 | ||
|
|
c96765f962 | ||
|
|
3830c7d1f3 | ||
|
|
05add001fb | ||
|
|
abc3ff3529 | ||
|
|
737be6c458 | ||
|
|
2e8abb7bb2 | ||
|
|
e2d75ead27 | ||
|
|
28e89a373f | ||
|
|
b4451da2f4 | ||
|
|
8353c399d2 | ||
|
|
af1b156ba6 | ||
|
|
3654743d95 | ||
|
|
58bd9b36cd | ||
|
|
d31b379b5c | ||
|
|
35c85bfeb8 | ||
|
|
64469061fd | ||
|
|
57c343910f | ||
|
|
319ba24be0 | ||
|
|
ec12470188 | ||
|
|
ec88e893c8 | ||
|
|
4ef5a0688b | ||
|
|
86438d7ad3 | ||
|
|
3b15a166c5 | ||
|
|
907a0fb4ff | ||
|
|
8b28bf66fa | ||
|
|
6bd7eadd85 | ||
|
|
a46a570c8a | ||
|
|
a64acb3126 | ||
|
|
2229f38602 | ||
|
|
901bf50d54 | ||
|
|
6f9d8d6351 | ||
|
|
e6874cb219 | ||
|
|
b4b5a2419b | ||
|
|
c888cef023 | ||
|
|
0e807d72ef | ||
|
|
3799ab529f | ||
|
|
65370a19a2 | ||
|
|
2dbd58860e | ||
|
|
63bfcc2011 | ||
|
|
ee3496255b | ||
|
|
db945bc833 | ||
|
|
fc40aaca63 | ||
|
|
313116ebcb | ||
|
|
1de33cf36c | ||
|
|
fcb3d48a1f | ||
|
|
6834067e7a | ||
|
|
d4b120b5f8 | ||
|
|
f0310a4177 | ||
|
|
06a53de350 | ||
|
|
bb99f9ff83 | ||
|
|
72b7b1b838 | ||
|
|
cbaea9cca9 | ||
|
|
5605e4d3bb | ||
|
|
bf0958f588 | ||
|
|
0ae862a84a | ||
|
|
3e216872ce | ||
|
|
1b26fa40f2 | ||
|
|
672389d6b8 | ||
|
|
888ef104a2 | ||
|
|
f90db3cff7 | ||
|
|
e0f553c15a | ||
|
|
89d319332a | ||
|
|
225e797176 | ||
|
|
5e28023853 | ||
|
|
4ef118c4eb | ||
|
|
92401b9e7f | ||
|
|
c78f924522 | ||
|
|
630f31c3f8 | ||
|
|
7dca87b377 | ||
|
|
8619b1cf59 | ||
|
|
c6c6cbeaa9 | ||
|
|
c17b4e29ff | ||
|
|
d2d8e2ef42 | ||
|
|
62e96e3f94 | ||
|
|
f95eea07fb | ||
|
|
1bd7e420e5 | ||
|
|
76cbf94fdc | ||
|
|
b2da426cc1 | ||
|
|
cce4693aea | ||
|
|
1c82523dc5 | ||
|
|
2241e2c1f2 | ||
|
|
c11076ee94 | ||
|
|
9273b163a8 | ||
|
|
5e0adc6234 | ||
|
|
e5f39b79f7 | ||
|
|
b98f8627ac |
29
.github/pull_request_template.md
vendored
29
.github/pull_request_template.md
vendored
@@ -1,15 +1,34 @@
|
|||||||
# Description
|
# Description of Changes
|
||||||
|
|
||||||
Please provide a summary of the changes, including relevant motivation and context.
|
Please provide a summary of the changes, including:
|
||||||
|
|
||||||
|
- What was changed
|
||||||
|
- Why the change was made
|
||||||
|
- Any challenges encountered
|
||||||
|
|
||||||
Closes #(issue_number)
|
Closes #(issue_number)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
- [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
|
- [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
|
||||||
|
- [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable)
|
||||||
|
- [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable)
|
||||||
- [ ] I have performed a self-review of my own code
|
- [ ] I have performed a self-review of my own code
|
||||||
- [ ] I have attached images of the change if it is UI based
|
|
||||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
||||||
- [ ] If my code has heavily changed functionality I have updated relevant docs on [Stirling-PDFs doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
|
|
||||||
- [ ] My changes generate no new warnings
|
- [ ] My changes generate no new warnings
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed)
|
||||||
- [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
|
- [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
|
||||||
|
|
||||||
|
### UI Changes (if applicable)
|
||||||
|
|
||||||
|
- [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR)
|
||||||
|
|
||||||
|
### Testing (if applicable)
|
||||||
|
|
||||||
|
- [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.
|
||||||
|
|||||||
51
.github/scripts/check_duplicates.py
vendored
51
.github/scripts/check_duplicates.py
vendored
@@ -1,51 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def find_duplicate_keys(file_path):
|
|
||||||
"""
|
|
||||||
Finds duplicate keys in a properties file and returns their occurrences.
|
|
||||||
|
|
||||||
This function reads a properties file, identifies any keys that occur more than
|
|
||||||
once, and returns a dictionary with these keys and the line numbers of their occurrences.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
file_path (str): The path to the properties file to be checked.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: A dictionary where each key is a duplicated key in the file, and the value is a list
|
|
||||||
of line numbers where the key occurs.
|
|
||||||
"""
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
lines = file.readlines()
|
|
||||||
|
|
||||||
keys = {}
|
|
||||||
duplicates = {}
|
|
||||||
|
|
||||||
for line_number, line in enumerate(lines, start=1):
|
|
||||||
line = line.strip()
|
|
||||||
if line and not line.startswith("#") and "=" in line:
|
|
||||||
key = line.split("=", 1)[0].strip()
|
|
||||||
if key in keys:
|
|
||||||
# If the key already exists, add the current line number
|
|
||||||
duplicates.setdefault(key, []).append(line_number)
|
|
||||||
# Also add the first instance of the key if not already done
|
|
||||||
if keys[key] not in duplicates[key]:
|
|
||||||
duplicates[key].insert(0, keys[key])
|
|
||||||
else:
|
|
||||||
# Store the line number of the first instance of the key
|
|
||||||
keys[key] = line_number
|
|
||||||
|
|
||||||
return duplicates
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
failed = False
|
|
||||||
for ar in sys.argv[1:]:
|
|
||||||
duplicates = find_duplicate_keys(ar)
|
|
||||||
if duplicates:
|
|
||||||
for key, lines in duplicates.items():
|
|
||||||
lines_str = ", ".join(map(str, lines))
|
|
||||||
print(f"{key} duplicated in {ar} on lines {lines_str}")
|
|
||||||
failed = True
|
|
||||||
if failed:
|
|
||||||
sys.exit(1)
|
|
||||||
107
.github/scripts/check_language_properties.py
vendored
107
.github/scripts/check_language_properties.py
vendored
@@ -11,6 +11,8 @@ adjusting the format.
|
|||||||
Usage:
|
Usage:
|
||||||
python check_language_properties.py --reference-file <path_to_reference_file> --branch <branch_name> [--actor <actor_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>]
|
||||||
"""
|
"""
|
||||||
|
# Sample for Windows:
|
||||||
|
# python .github/scripts/check_language_properties.py --reference-file src\main\resources\messages_en_GB.properties --branch "" --files src\main\resources\messages_de_DE.properties src\main\resources\messages_uk_UA.properties
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import glob
|
import glob
|
||||||
@@ -19,25 +21,60 @@ import argparse
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def find_duplicate_keys(file_path):
|
||||||
|
"""
|
||||||
|
Identifies duplicate keys in a .properties file.
|
||||||
|
:param file_path: Path to the .properties file.
|
||||||
|
:return: List of tuples (key, first_occurrence_line, duplicate_line).
|
||||||
|
"""
|
||||||
|
keys = {}
|
||||||
|
duplicates = []
|
||||||
|
|
||||||
|
with open(file_path, "r", encoding="utf-8") as file:
|
||||||
|
for line_number, line in enumerate(file, start=1):
|
||||||
|
stripped_line = line.strip()
|
||||||
|
|
||||||
|
# Skip empty lines and comments
|
||||||
|
if not stripped_line or stripped_line.startswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Split the line into key and value
|
||||||
|
if "=" in stripped_line:
|
||||||
|
key, _ = stripped_line.split("=", 1)
|
||||||
|
key = key.strip()
|
||||||
|
|
||||||
|
# Check if the key already exists
|
||||||
|
if key in keys:
|
||||||
|
duplicates.append((key, keys[key], line_number))
|
||||||
|
else:
|
||||||
|
keys[key] = line_number
|
||||||
|
|
||||||
|
return duplicates
|
||||||
|
|
||||||
|
|
||||||
# Maximum size for properties files (e.g., 200 KB)
|
# Maximum size for properties files (e.g., 200 KB)
|
||||||
MAX_FILE_SIZE = 200 * 1024
|
MAX_FILE_SIZE = 200 * 1024
|
||||||
|
|
||||||
|
|
||||||
def parse_properties_file(file_path):
|
def parse_properties_file(file_path):
|
||||||
"""Parses a .properties file and returns a list of objects (including comments, empty lines, and line numbers)."""
|
"""
|
||||||
|
Parses a .properties file and returns a structured list of its contents.
|
||||||
|
:param file_path: Path to the .properties file.
|
||||||
|
:return: List of dictionaries representing each line in the file.
|
||||||
|
"""
|
||||||
properties_list = []
|
properties_list = []
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
with open(file_path, "r", encoding="utf-8") as file:
|
||||||
for line_number, line in enumerate(file, start=1):
|
for line_number, line in enumerate(file, start=1):
|
||||||
stripped_line = line.strip()
|
stripped_line = line.strip()
|
||||||
|
|
||||||
# Empty lines
|
# Handle empty lines
|
||||||
if not stripped_line:
|
if not stripped_line:
|
||||||
properties_list.append(
|
properties_list.append(
|
||||||
{"line_number": line_number, "type": "empty", "content": ""}
|
{"line_number": line_number, "type": "empty", "content": ""}
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Comments
|
# Handle comments
|
||||||
if stripped_line.startswith("#"):
|
if stripped_line.startswith("#"):
|
||||||
properties_list.append(
|
properties_list.append(
|
||||||
{
|
{
|
||||||
@@ -48,7 +85,7 @@ def parse_properties_file(file_path):
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Key-value pairs
|
# Handle key-value pairs
|
||||||
match = re.match(r"^([^=]+)=(.*)$", line)
|
match = re.match(r"^([^=]+)=(.*)$", line)
|
||||||
if match:
|
if match:
|
||||||
key, value = match.groups()
|
key, value = match.groups()
|
||||||
@@ -65,9 +102,14 @@ def parse_properties_file(file_path):
|
|||||||
|
|
||||||
|
|
||||||
def write_json_file(file_path, updated_properties):
|
def write_json_file(file_path, updated_properties):
|
||||||
|
"""
|
||||||
|
Writes updated properties back to the file in their original format.
|
||||||
|
:param file_path: Path to the .properties file.
|
||||||
|
:param updated_properties: List of updated properties to write.
|
||||||
|
"""
|
||||||
updated_lines = {entry["line_number"]: entry for entry in updated_properties}
|
updated_lines = {entry["line_number"]: entry for entry in updated_properties}
|
||||||
|
|
||||||
# Sort by line numbers and retain comments and empty lines
|
# Sort lines by their numbers and retain comments and empty lines
|
||||||
all_lines = sorted(set(updated_lines.keys()))
|
all_lines = sorted(set(updated_lines.keys()))
|
||||||
|
|
||||||
original_format = []
|
original_format = []
|
||||||
@@ -86,8 +128,8 @@ def write_json_file(file_path, updated_properties):
|
|||||||
# Replace entries with those from the current JSON
|
# Replace entries with those from the current JSON
|
||||||
original_format.append(entry)
|
original_format.append(entry)
|
||||||
|
|
||||||
# Write back in the original format
|
# Write the updated content back to the file
|
||||||
with open(file_path, "w", encoding="utf-8") as file:
|
with open(file_path, "w", encoding="utf-8", newline="\n") as file:
|
||||||
for entry in original_format:
|
for entry in original_format:
|
||||||
if entry["type"] == "comment":
|
if entry["type"] == "comment":
|
||||||
file.write(f"{entry['content']}\n")
|
file.write(f"{entry['content']}\n")
|
||||||
@@ -98,6 +140,12 @@ def write_json_file(file_path, updated_properties):
|
|||||||
|
|
||||||
|
|
||||||
def update_missing_keys(reference_file, file_list, branch=""):
|
def update_missing_keys(reference_file, file_list, branch=""):
|
||||||
|
"""
|
||||||
|
Updates missing keys in the translation files based on the reference file.
|
||||||
|
:param reference_file: Path to the reference .properties file.
|
||||||
|
:param file_list: List of translation files to update.
|
||||||
|
:param branch: Branch where the files are located.
|
||||||
|
"""
|
||||||
reference_properties = parse_properties_file(reference_file)
|
reference_properties = parse_properties_file(reference_file)
|
||||||
for file_path in file_list:
|
for file_path in file_list:
|
||||||
basename_current_file = os.path.basename(os.path.join(branch, file_path))
|
basename_current_file = os.path.basename(os.path.join(branch, file_path))
|
||||||
@@ -164,8 +212,14 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
|||||||
basename_current_file = os.path.basename(os.path.join(branch, file_path))
|
basename_current_file = os.path.basename(os.path.join(branch, file_path))
|
||||||
if (
|
if (
|
||||||
basename_current_file == basename_reference_file
|
basename_current_file == basename_reference_file
|
||||||
or not file_path.startswith(
|
or (
|
||||||
os.path.join("src", "main", "resources", "messages_")
|
# only local windows command
|
||||||
|
not file_path.startswith(
|
||||||
|
os.path.join("", "src", "main", "resources", "messages_")
|
||||||
|
)
|
||||||
|
and not file_path.startswith(
|
||||||
|
os.path.join(os.getcwd(), "src", "main", "resources", "messages_")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
or not file_path.endswith(".properties")
|
or not file_path.endswith(".properties")
|
||||||
or not basename_current_file.startswith("messages_")
|
or not basename_current_file.startswith("messages_")
|
||||||
@@ -237,6 +291,24 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
report.append("2. **Test Status:** ✅ **_Passed_**")
|
report.append("2. **Test Status:** ✅ **_Passed_**")
|
||||||
|
|
||||||
|
if find_duplicate_keys(os.path.join(branch, file_path)):
|
||||||
|
has_differences = True
|
||||||
|
output = "\n".join(
|
||||||
|
[
|
||||||
|
f" - `{key}`: first at line {first}, duplicate at `line {duplicate}`"
|
||||||
|
for key, first, duplicate in find_duplicate_keys(
|
||||||
|
os.path.join(branch, file_path)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
report.append("3. **Test Status:** ❌ **_Failed_**")
|
||||||
|
report.append(" - **Issue:**")
|
||||||
|
report.append(" - duplicate entries were found:")
|
||||||
|
report.append(output)
|
||||||
|
else:
|
||||||
|
report.append("3. **Test Status:** ✅ **_Passed_**")
|
||||||
|
|
||||||
report.append("")
|
report.append("")
|
||||||
report.append("---")
|
report.append("---")
|
||||||
report.append("")
|
report.append("")
|
||||||
@@ -275,6 +347,12 @@ if __name__ == "__main__":
|
|||||||
required=True,
|
required=True,
|
||||||
help="Branch name.",
|
help="Branch name.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--check-file",
|
||||||
|
type=str,
|
||||||
|
required=False,
|
||||||
|
help="List of changed files, separated by spaces.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--files",
|
"--files",
|
||||||
nargs="+",
|
nargs="+",
|
||||||
@@ -293,11 +371,14 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
file_list = args.files
|
file_list = args.files
|
||||||
if file_list is None:
|
if file_list is None:
|
||||||
file_list = glob.glob(
|
if args.check_file:
|
||||||
os.path.join(
|
file_list = [args.check_file]
|
||||||
os.getcwd(), "src", "main", "resources", "messages_*.properties"
|
else:
|
||||||
|
file_list = glob.glob(
|
||||||
|
os.path.join(
|
||||||
|
os.getcwd(), "src", "main", "resources", "messages_*.properties"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
update_missing_keys(args.reference_file, file_list)
|
update_missing_keys(args.reference_file, file_list)
|
||||||
else:
|
else:
|
||||||
check_for_differences(args.reference_file, file_list, args.branch, args.actor)
|
check_for_differences(args.reference_file, file_list, args.branch, args.actor)
|
||||||
|
|||||||
85
.github/scripts/check_tabulator.py
vendored
85
.github/scripts/check_tabulator.py
vendored
@@ -1,85 +0,0 @@
|
|||||||
"""check_tabulator.py"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def check_tabs(file_path):
|
|
||||||
"""
|
|
||||||
Checks for tabs in the specified file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path (str): The path to the file to be checked.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if tabs are found, False otherwise.
|
|
||||||
"""
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
content = file.read()
|
|
||||||
|
|
||||||
if "\t" in content:
|
|
||||||
print(f"Tab found in {file_path}")
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def replace_tabs_with_spaces(file_path, replace_with=" "):
|
|
||||||
"""
|
|
||||||
Replaces tabs with a specified number of spaces in the file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path (str): The path to the file where tabs will be replaced.
|
|
||||||
replace_with (str): The character(s) to replace tabs with. Defaults to two spaces.
|
|
||||||
"""
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
content = file.read()
|
|
||||||
|
|
||||||
updated_content = content.replace("\t", replace_with)
|
|
||||||
|
|
||||||
with open(file_path, "w", encoding="utf-8") as file:
|
|
||||||
file.write(updated_content)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
Main function to replace tabs with spaces in the provided files.
|
|
||||||
The replacement character and files to check are taken from command line arguments.
|
|
||||||
"""
|
|
||||||
# Create ArgumentParser instance
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Replace tabs in files with specified characters."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define optional argument `--replace_with`
|
|
||||||
parser.add_argument(
|
|
||||||
"--replace_with",
|
|
||||||
default=" ",
|
|
||||||
help="Character(s) to replace tabs with. Default is two spaces.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define argument for file paths
|
|
||||||
parser.add_argument("files", metavar="FILE", nargs="+", help="Files to process.")
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# Extract replacement characters and files from the parsed arguments
|
|
||||||
replace_with = args.replace_with
|
|
||||||
files_checked = args.files
|
|
||||||
|
|
||||||
error = False
|
|
||||||
|
|
||||||
for file_path in files_checked:
|
|
||||||
if check_tabs(file_path):
|
|
||||||
replace_tabs_with_spaces(file_path, replace_with)
|
|
||||||
error = True
|
|
||||||
|
|
||||||
if error:
|
|
||||||
print("Error: Originally found tabs in HTML files, now replaced.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
1
.github/scripts/requirements_pre_commit.in
vendored
Normal file
1
.github/scripts/requirements_pre_commit.in
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pre-commit
|
||||||
93
.github/scripts/requirements_pre_commit.txt
vendored
Normal file
93
.github/scripts/requirements_pre_commit.txt
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile with Python 3.10
|
||||||
|
# by the following command:
|
||||||
|
#
|
||||||
|
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_pre_commit.txt' '.github\scripts\requirements_pre_commit.in'
|
||||||
|
#
|
||||||
|
cfgv==3.4.0 \
|
||||||
|
--hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \
|
||||||
|
--hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560
|
||||||
|
# via pre-commit
|
||||||
|
distlib==0.3.9 \
|
||||||
|
--hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \
|
||||||
|
--hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403
|
||||||
|
# via virtualenv
|
||||||
|
filelock==3.16.1 \
|
||||||
|
--hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \
|
||||||
|
--hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435
|
||||||
|
# via virtualenv
|
||||||
|
identify==2.6.5 \
|
||||||
|
--hash=sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566 \
|
||||||
|
--hash=sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc
|
||||||
|
# via pre-commit
|
||||||
|
nodeenv==1.9.1 \
|
||||||
|
--hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \
|
||||||
|
--hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9
|
||||||
|
# via pre-commit
|
||||||
|
platformdirs==4.3.6 \
|
||||||
|
--hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \
|
||||||
|
--hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb
|
||||||
|
# via virtualenv
|
||||||
|
pre-commit==4.0.1 \
|
||||||
|
--hash=sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2 \
|
||||||
|
--hash=sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878
|
||||||
|
# via -r .github\scripts\requirements_pre_commit.in
|
||||||
|
pyyaml==6.0.2 \
|
||||||
|
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||||
|
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||||
|
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||||
|
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||||
|
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||||
|
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||||
|
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||||
|
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||||
|
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||||
|
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||||
|
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||||
|
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||||
|
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||||
|
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||||
|
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||||
|
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||||
|
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||||
|
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||||
|
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||||
|
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||||
|
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||||
|
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||||
|
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||||
|
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||||
|
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||||
|
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||||
|
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||||
|
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||||
|
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||||
|
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||||
|
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||||
|
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||||
|
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||||
|
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||||
|
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||||
|
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||||
|
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||||
|
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||||
|
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||||
|
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||||
|
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||||
|
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||||
|
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||||
|
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||||
|
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||||
|
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||||
|
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||||
|
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||||
|
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||||
|
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||||
|
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||||
|
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||||
|
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||||
|
# via pre-commit
|
||||||
|
virtualenv==20.28.1 \
|
||||||
|
--hash=sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb \
|
||||||
|
--hash=sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329
|
||||||
|
# via pre-commit
|
||||||
1
.github/scripts/requirements_sync_readme.in
vendored
Normal file
1
.github/scripts/requirements_sync_readme.in
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
tomlkit
|
||||||
10
.github/scripts/requirements_sync_readme.txt
vendored
Normal file
10
.github/scripts/requirements_sync_readme.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile with Python 3.10
|
||||||
|
# by the following command:
|
||||||
|
#
|
||||||
|
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_sync_readme.txt' '.github\scripts\requirements_sync_readme.in'
|
||||||
|
#
|
||||||
|
tomlkit==0.13.2 \
|
||||||
|
--hash=sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde \
|
||||||
|
--hash=sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79
|
||||||
|
# via -r .github\scripts\requirements_sync_readme.in
|
||||||
10
.github/workflows/PR-Demo-Comment.yml
vendored
10
.github/workflows/PR-Demo-Comment.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -95,8 +95,8 @@ jobs:
|
|||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: "17"
|
||||||
distribution: 'temurin'
|
distribution: "temurin"
|
||||||
|
|
||||||
- name: Run Gradle Command
|
- name: Run Gradle Command
|
||||||
run: ./gradlew clean build
|
run: ./gradlew clean build
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
password: ${{ secrets.DOCKER_HUB_API }}
|
password: ${{ secrets.DOCKER_HUB_API }}
|
||||||
|
|
||||||
- name: Build and push PR-specific image
|
- name: Build and push PR-specific image
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
|
|||||||
6
.github/workflows/PR-Demo-cleanup.yml
vendored
6
.github/workflows/PR-Demo-cleanup.yml
vendored
@@ -8,8 +8,8 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
||||||
CLEANUP_PERFORMED: 'false' # Add flag to track if cleanup occurred
|
CLEANUP_PERFORMED: "false" # Add flag to track if cleanup occurred
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cleanup:
|
cleanup:
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/auto-labeler.yml
vendored
2
.github/workflows/auto-labeler.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -37,6 +37,12 @@ jobs:
|
|||||||
java-version: ${{ matrix.jdk-version }}
|
java-version: ${{ matrix.jdk-version }}
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
|
- name: PR | Generate verification metadata with signatures and checksums for dependabot[bot]
|
||||||
|
if: github.event.pull_request.user.login == 'dependabot[bot]'
|
||||||
|
run: |
|
||||||
|
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 --refresh-dependencies help
|
||||||
|
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help
|
||||||
|
|
||||||
- name: Build with Gradle and no spring security
|
- name: Build with Gradle and no spring security
|
||||||
run: ./gradlew clean build
|
run: ./gradlew clean build
|
||||||
env:
|
env:
|
||||||
@@ -49,7 +55,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Test Reports
|
- name: Upload Test Reports
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
name: test-reports-jdk-${{ matrix.jdk-version }}
|
name: test-reports-jdk-${{ matrix.jdk-version }}
|
||||||
path: |
|
path: |
|
||||||
@@ -77,7 +83,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -95,19 +101,21 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Docker Compose
|
- name: Install Docker Compose
|
||||||
run: |
|
run: |
|
||||||
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
sudo chmod +x /usr/local/bin/docker-compose
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|
||||||
- name: Pip requirements
|
- name: Pip requirements
|
||||||
run: |
|
run: |
|
||||||
pip install --require-hashes -r ./cucumber/requirements.txt
|
pip install --require-hashes -r ./testing/cucumber/requirements.txt
|
||||||
|
|
||||||
- name: Run Docker Compose Tests
|
- name: Run Docker Compose Tests
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./test.sh
|
chmod +x ./testing/test_webpages.sh
|
||||||
./test.sh
|
chmod +x ./testing/test.sh
|
||||||
|
./testing/test.sh "${{ github.event.pull_request.user.login == 'dependabot[bot]' }}"
|
||||||
|
|||||||
6
.github/workflows/check_properties.yml
vendored
6
.github/workflows/check_properties.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "Fetching PR changed files..."
|
echo "Fetching PR changed files..."
|
||||||
echo "Getting list of changed files from PR..."
|
echo "Getting list of changed files from PR..."
|
||||||
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > changed_files.txt # Filter only matching property files
|
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$' > changed_files.txt # Filter only matching property files
|
||||||
|
|
||||||
- name: Determine reference file test
|
- name: Determine reference file test
|
||||||
id: determine-file
|
id: determine-file
|
||||||
@@ -99,7 +99,7 @@ jobs:
|
|||||||
// Filter for relevant files based on the PR changes
|
// Filter for relevant files based on the PR changes
|
||||||
const changedFiles = files
|
const changedFiles = files
|
||||||
.map(file => file.filename)
|
.map(file => file.filename)
|
||||||
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]+\.properties$/.test(file));
|
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
|
||||||
|
|
||||||
console.log("Changed files:", changedFiles);
|
console.log("Changed files:", changedFiles);
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/codeql.yml-disabled
vendored
2
.github/workflows/codeql.yml-disabled
vendored
@@ -42,7 +42,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
8
.github/workflows/dependency-review.yml
vendored
8
.github/workflows/dependency-review.yml
vendored
@@ -6,7 +6,7 @@
|
|||||||
# PRs introducing known-vulnerable packages will be blocked from merging.
|
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||||
#
|
#
|
||||||
# Source repository: https://github.com/actions/dependency-review-action
|
# Source repository: https://github.com/actions/dependency-review-action
|
||||||
name: 'Dependency Review'
|
name: "Dependency Review"
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -17,11 +17,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: 'Checkout Repository'
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: 'Dependency Review'
|
- name: "Dependency Review"
|
||||||
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||||
|
|||||||
37
.github/workflows/licenses-update.yml
vendored
37
.github/workflows/licenses-update.yml
vendored
@@ -18,10 +18,17 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Generate GitHub App Token
|
||||||
|
id: generate-token
|
||||||
|
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.GH_APP_ID }}
|
||||||
|
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
@@ -42,8 +49,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Set up git config
|
- name: Set up git config
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name "github-actions[bot]"
|
git config --global user.name "stirlingbot[bot]"
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "1113334+stirlingbot[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
- name: Run git add
|
- name: Run git add
|
||||||
run: |
|
run: |
|
||||||
@@ -55,32 +62,22 @@ jobs:
|
|||||||
if: env.CHANGES_DETECTED == 'true'
|
if: env.CHANGES_DETECTED == 'true'
|
||||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
commit-message: "Update 3rd Party Licenses"
|
commit-message: "Update 3rd Party Licenses"
|
||||||
committer: GitHub Action <action@github.com>
|
committer: "stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>"
|
||||||
author: GitHub Action <action@github.com>
|
author: "stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>"
|
||||||
signoff: true
|
signoff: true
|
||||||
branch: update-3rd-party-licenses
|
branch: update-3rd-party-licenses
|
||||||
title: "Update 3rd Party Licenses"
|
title: "Update 3rd Party Licenses"
|
||||||
body: |
|
body: |
|
||||||
Auto-generated by [create-pull-request][1]
|
Auto-generated by StirlingBot
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
|
||||||
labels: licenses,github-actions
|
labels: licenses,github-actions
|
||||||
draft: false
|
draft: false
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
sign-commits: true
|
sign-commits: true
|
||||||
|
|
||||||
- name: Auto approve
|
- name: Enable Pull Request Automerge
|
||||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||||
run: gh pr review --approve "${{ steps.cpr.outputs.pull-request-number }}"
|
run: gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}"
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Enable auto-merge
|
|
||||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
|
||||||
uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3.0.0
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
|
||||||
merge-method: squash # Choose the merge method: merge, squash, or rebase
|
|
||||||
|
|||||||
2
.github/workflows/manage-label.yml
vendored
2
.github/workflows/manage-label.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
261
.github/workflows/multiOSReleases.yml
vendored
261
.github/workflows/multiOSReleases.yml
vendored
@@ -9,27 +9,137 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
read_versions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
|
versionMac: ${{ steps.versionNumberMac.outputs.versionNumberMac }}
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
# Get version number
|
||||||
|
- name: Get version number
|
||||||
|
id: versionNumber
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||||
|
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Get version number mac
|
||||||
|
id: versionNumberMac
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||||
|
CURRENT_YEAR=$(date +'%Y')
|
||||||
|
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
|
||||||
|
MAC_VERSION="$CURRENT_YEAR.${VERSION_PARTS[1]:-0}.${VERSION_PARTS[2]:-0}"
|
||||||
|
echo "versionNumberMac=$MAC_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build-portable:
|
||||||
|
needs: read_versions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
enable_security: [true, false]
|
||||||
|
include:
|
||||||
|
- enable_security: true
|
||||||
|
file_suffix: "-with-login"
|
||||||
|
- enable_security: false
|
||||||
|
file_suffix: ""
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
|
with:
|
||||||
|
java-version: "21"
|
||||||
|
distribution: "temurin"
|
||||||
|
|
||||||
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
|
with:
|
||||||
|
gradle-version: 8.12
|
||||||
|
|
||||||
|
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||||
|
run: ./gradlew clean createExe
|
||||||
|
env:
|
||||||
|
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
|
||||||
|
STIRLING_PDF_DESKTOP_UI: false
|
||||||
|
|
||||||
|
- name: Rename binaries
|
||||||
|
run: |
|
||||||
|
mkdir ./binaries
|
||||||
|
mv ./build/launch4j/Stirling-PDF.exe ./binaries/win-Stirling-PDF-portable-Server${{ matrix.file_suffix }}.exe
|
||||||
|
mv ./build/libs/Stirling-PDF-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
|
with:
|
||||||
|
retention-days: 1
|
||||||
|
if-no-files-found: error
|
||||||
|
name: stirling${{ matrix.file_suffix }}-binaries
|
||||||
|
path: |
|
||||||
|
./binaries/*
|
||||||
|
|
||||||
|
sign_verify-portable:
|
||||||
|
needs: [build-portable, read_versions]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
enable_security: [true, false]
|
||||||
|
include:
|
||||||
|
- enable_security: true
|
||||||
|
file_suffix: "with-login-"
|
||||||
|
- enable_security: false
|
||||||
|
file_suffix: ""
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
with:
|
||||||
|
name: stirling-${{ matrix.file_suffix }}binaries
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Upload signed artifacts
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
|
with:
|
||||||
|
retention-days: 1
|
||||||
|
if-no-files-found: error
|
||||||
|
name: stirling-${{ matrix.file_suffix }}signed
|
||||||
|
path: |
|
||||||
|
./*
|
||||||
|
!cosign.*
|
||||||
|
|
||||||
build-installers:
|
build-installers:
|
||||||
|
needs: read_versions
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
platform: win
|
platform: win-
|
||||||
ext: exe
|
- os: macos-latest
|
||||||
#- os: macos-latest
|
platform: mac-
|
||||||
# platform: mac
|
# - os: ubuntu-latest
|
||||||
# ext: dmg
|
# platform: linux-
|
||||||
#- os: ubuntu-latest
|
|
||||||
# platform: linux
|
|
||||||
# ext: deb
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -52,54 +162,127 @@ jobs:
|
|||||||
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
|
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
|
||||||
.\wix.exe /install /quiet
|
.\wix.exe /install /quiet
|
||||||
|
|
||||||
# Install Linux dependencies
|
|
||||||
- name: Install Linux Dependencies
|
|
||||||
if: matrix.os == 'ubuntu-latest'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y fakeroot rpm
|
|
||||||
|
|
||||||
# Get version number
|
|
||||||
- name: Get version number
|
|
||||||
id: versionNumber
|
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Get version number mac
|
|
||||||
id: versionNumberMac
|
|
||||||
run: echo "versionNumberMac=$(./gradlew printMacVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
# Build installer
|
# Build installer
|
||||||
- name: Build Installer
|
- name: Build Installer
|
||||||
run: ./gradlew build jpackage -x test --info
|
run: ./gradlew build jpackage -x test --info
|
||||||
env:
|
env:
|
||||||
DOCKER_ENABLE_SECURITY: false
|
DOCKER_ENABLE_SECURITY: false
|
||||||
STIRLING_PDF_DESKTOP_UI: true
|
STIRLING_PDF_DESKTOP_UI: true
|
||||||
|
BROWSER_OPEN: true
|
||||||
|
|
||||||
# Rename and collect artifacts based on OS
|
# Rename and collect artifacts based on OS
|
||||||
- name: Prepare artifacts
|
- name: Prepare artifacts
|
||||||
id: prepare
|
id: prepare
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
mkdir ./binaries
|
||||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.exe" "Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}"
|
mv "./build/jpackage/Stirling-PDF-${{ needs.read_versions.outputs.version }}.exe" "./binaries/Stirling-PDF-win-installer.exe"
|
||||||
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
|
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
|
||||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumberMac.outputs.versionNumberMac }}.dmg" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
mv "./build/jpackage/Stirling-PDF-${{ needs.read_versions.outputs.versionMac }}.dmg" "./binaries/Stirling-PDF-mac-installer.dmg"
|
||||||
else
|
else
|
||||||
mv "build/jpackage/stirling-pdf_${{ steps.versionNumber.outputs.versionNumber }}-1_amd64.deb" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
mv "./build/jpackage/stirling-pdf_${{ needs.read_versions.outputs.version }}-1_amd64.deb" "./binaries/Stirling-PDF-linux-installer.deb"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Upload installer as artifact for testing
|
- name: Display structure of downloaded files
|
||||||
- name: Upload Installer Artifact
|
run: ls -R ./binaries
|
||||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
name: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
|
||||||
path: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
name: ${{ matrix.platform }}binaries
|
||||||
|
path: |
|
||||||
|
./binaries/*
|
||||||
|
|
||||||
- name: Upload binaries to release
|
sign_verify:
|
||||||
|
needs: [read_versions, build-installers]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
platform: win-
|
||||||
|
- os: macos-latest
|
||||||
|
platform: mac-
|
||||||
|
# - os: ubuntu-latest
|
||||||
|
# platform: linux-
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.platform }}binaries
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Install Cosign
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||||
|
|
||||||
|
- name: Generate key pair
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
run: cosign generate-key-pair
|
||||||
|
|
||||||
|
- name: Sign and generate attestations
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
run: |
|
||||||
|
cosign sign-blob \
|
||||||
|
--key ./cosign.key \
|
||||||
|
--yes \
|
||||||
|
--output-signature ./Stirling-PDF-win-installer.exe.sig \
|
||||||
|
./Stirling-PDF-win-installer.exe
|
||||||
|
|
||||||
|
cosign attest-blob \
|
||||||
|
--predicate - \
|
||||||
|
--key ./cosign.key \
|
||||||
|
--yes \
|
||||||
|
--output-attestation ./Stirling-PDF-win-installer.exe.intoto.jsonl \
|
||||||
|
./Stirling-PDF-win-installer.exe
|
||||||
|
|
||||||
|
cosign verify-blob \
|
||||||
|
--key ./cosign.pub \
|
||||||
|
--signature ./Stirling-PDF-win-installer.exe.sig \
|
||||||
|
./Stirling-PDF-win-installer.exe
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Upload signed artifacts
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
|
with:
|
||||||
|
retention-days: 1
|
||||||
|
if-no-files-found: error
|
||||||
|
name: ${{ matrix.platform }}signed
|
||||||
|
path: |
|
||||||
|
./Stirling-PDF-${{ matrix.platform }}installer.*
|
||||||
|
!cosign.*
|
||||||
|
|
||||||
|
create-release:
|
||||||
|
needs: [read_versions, sign_verify, sign_verify-portable]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Download signed artifacts
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
- name: Upload binaries, attestations and signatures to Release and create GitHub Release
|
||||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||||
with:
|
with:
|
||||||
files: ./Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
tag_name: v${{ needs.read_versions.outputs.version }}
|
||||||
|
generate_release_notes: true
|
||||||
|
files: |
|
||||||
|
./*signed/*
|
||||||
|
|||||||
55
.github/workflows/pre_commit.yml
vendored
55
.github/workflows/pre_commit.yml
vendored
@@ -1,20 +1,42 @@
|
|||||||
name: Pre-commit
|
name: Pre-commit
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch:
|
||||||
branches: [main]
|
schedule:
|
||||||
|
- cron: "0 0 * * 1"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update:
|
pre-commit:
|
||||||
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Generate GitHub App Token
|
||||||
|
id: generate-token
|
||||||
|
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.GH_APP_ID }}
|
||||||
|
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Get GitHub App User ID
|
||||||
|
id: get-user-id
|
||||||
|
run: echo "user-id=$(gh api "/users/${{ steps.generate-token.outputs.app-slug }}[bot]" --jq .id)" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
|
- id: committer
|
||||||
|
run: |
|
||||||
|
echo "string=${{ steps.generate-token.outputs.app-slug }}[bot] <${{ steps.get-user-id.outputs.user-id }}+${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com>" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
@@ -23,30 +45,33 @@ jobs:
|
|||||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.12
|
||||||
|
cache: 'pip' # caching pip dependencies
|
||||||
- name: Run Pre-Commit Hooks
|
- name: Run Pre-Commit Hooks
|
||||||
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
|
run: |
|
||||||
|
pip install --require-hashes -r ./.github/scripts/requirements_pre_commit.txt
|
||||||
|
- run: pre-commit run --all-files -c .pre-commit-config.yaml
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Set up git config
|
- name: Set up git config
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name "github-actions[bot]"
|
git config --global user.name ${{ steps.generate-token.outputs.app-slug }}[bot]
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com"
|
||||||
- name: git add
|
- name: git add
|
||||||
run: |
|
run: |
|
||||||
git add .
|
git add .
|
||||||
git diff --staged --quiet || git commit -m ":file_folder: pre-commit
|
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
||||||
> Made via .github/workflows/pre_commit.yml" || echo "pre-commit: no changes"
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
|
if: env.CHANGES_DETECTED == 'true'
|
||||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
commit-message: "ci: 🤖 format everything with pre-commit"
|
commit-message: ":file_folder: pre-commit"
|
||||||
committer: GitHub Action <action@github.com>
|
committer: ${{ steps.committer.outputs.string }}
|
||||||
author: GitHub Action <action@github.com>
|
author: ${{ steps.committer.outputs.string }}
|
||||||
signoff: true
|
signoff: true
|
||||||
branch: pre-commit
|
branch: pre-commit
|
||||||
title: "🤖 format everything with pre-commit by <github-actions[bot]>"
|
title: "🤖 format everything with pre-commit by <${{ steps.generate-token.outputs.app-slug }}>"
|
||||||
body: |
|
body: |
|
||||||
Auto-generated by [create-pull-request][1]
|
Auto-generated by [create-pull-request][1] with **${{ steps.generate-token.outputs.app-slug }}**
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
[1]: https://github.com/peter-evans/create-pull-request
|
||||||
draft: false
|
draft: false
|
||||||
|
|||||||
12
.github/workflows/push-docker.yml
vendored
12
.github/workflows/push-docker.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||||
with:
|
with:
|
||||||
cosign-release: 'v2.4.1'
|
cosign-release: "v2.4.1"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
password: ${{ github.token }}
|
password: ${{ github.token }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3.3.0
|
||||||
|
|
||||||
- name: Convert repository owner to lowercase
|
- name: Convert repository owner to lowercase
|
||||||
id: repoowner
|
id: repoowner
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push main Dockerfile
|
- name: Build and push main Dockerfile
|
||||||
id: build-push-regular
|
id: build-push-regular
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
@@ -134,7 +134,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push Dockerfile-ultra-lite
|
- name: Build and push Dockerfile-ultra-lite
|
||||||
id: build-push-lite
|
id: build-push-lite
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@@ -165,7 +165,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push main Dockerfile fat
|
- name: Build and push main Dockerfile fat
|
||||||
id: build-push-fat
|
id: build-push-fat
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
|
|||||||
163
.github/workflows/releaseArtifacts.yml
vendored
163
.github/workflows/releaseArtifacts.yml
vendored
@@ -9,11 +9,8 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
packages: write
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
enable_security: [true, false]
|
enable_security: [true, false]
|
||||||
@@ -22,9 +19,11 @@ jobs:
|
|||||||
file_suffix: "-with-login"
|
file_suffix: "-with-login"
|
||||||
- enable_security: false
|
- enable_security: false
|
||||||
file_suffix: ""
|
file_suffix: ""
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -48,38 +47,134 @@ jobs:
|
|||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
run: |
|
||||||
|
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||||
|
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Rename binarie
|
- name: Rename binaries
|
||||||
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
run: |
|
||||||
|
mv ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
|
mv ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|
||||||
- name: Upload Assets binarie
|
- name: Debug build artifacts
|
||||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
run: |
|
||||||
|
echo "Current Directory: $(pwd)"
|
||||||
|
ls -R ./build/libs
|
||||||
|
ls -R ./build/launch4j
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
path: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
name: binaries${{ matrix.file_suffix }}
|
||||||
name: Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
path: |
|
||||||
overwrite: true
|
./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.*
|
||||||
retention-days: 1
|
./build/libs/Stirling-PDF${{ matrix.file_suffix }}.*
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Upload binaries to release
|
sign_verify:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
enable_security: [true, false]
|
||||||
|
include:
|
||||||
|
- enable_security: true
|
||||||
|
file_suffix: "-with-login"
|
||||||
|
- enable_security: false
|
||||||
|
file_suffix: ""
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
with:
|
||||||
|
name: binaries${{ matrix.file_suffix }}
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Install Cosign
|
||||||
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||||
|
|
||||||
|
- name: Generate key pair
|
||||||
|
run: cosign generate-key-pair
|
||||||
|
|
||||||
|
- name: Sign and generate attestations
|
||||||
|
run: |
|
||||||
|
cosign sign-blob \
|
||||||
|
--key ./cosign.key \
|
||||||
|
--yes \
|
||||||
|
--output-signature ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.sig \
|
||||||
|
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|
||||||
|
cosign attest-blob \
|
||||||
|
--predicate - \
|
||||||
|
--key ./cosign.key \
|
||||||
|
--yes \
|
||||||
|
--output-attestation ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.intoto.jsonl \
|
||||||
|
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|
||||||
|
cosign verify-blob \
|
||||||
|
--key ./cosign.pub \
|
||||||
|
--signature ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.sig \
|
||||||
|
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|
||||||
|
cosign sign-blob \
|
||||||
|
--key ./cosign.key \
|
||||||
|
--yes \
|
||||||
|
--output-signature ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.sig \
|
||||||
|
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
|
|
||||||
|
cosign attest-blob \
|
||||||
|
--predicate - \
|
||||||
|
--key ./cosign.key \
|
||||||
|
--yes \
|
||||||
|
--output-attestation ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.intoto.jsonl \
|
||||||
|
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
|
|
||||||
|
cosign verify-blob \
|
||||||
|
--key ./cosign.pub \
|
||||||
|
--signature ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.sig \
|
||||||
|
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
|
|
||||||
|
- name: Upload signed artifacts
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
|
with:
|
||||||
|
name: signed${{ matrix.file_suffix }}
|
||||||
|
path: |
|
||||||
|
./libs/Stirling-PDF${{ matrix.file_suffix }}.*
|
||||||
|
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.*
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: [build, sign_verify]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
enable_security: [true, false]
|
||||||
|
include:
|
||||||
|
- enable_security: true
|
||||||
|
file_suffix: "-with-login"
|
||||||
|
- enable_security: false
|
||||||
|
file_suffix: ""
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Download signed artifacts
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
with:
|
||||||
|
name: signed${{ matrix.file_suffix }}
|
||||||
|
|
||||||
|
- name: Upload binaries, attestations and signatures to Release and create GitHub Release
|
||||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||||
with:
|
with:
|
||||||
files: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
tag_name: v${{ needs.build.outputs.version }}
|
||||||
|
generate_release_notes: true
|
||||||
- name: Rename jar binaries
|
files: |
|
||||||
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
./libs/Stirling-PDF*
|
||||||
|
./launch4j/Stirling-PDF-Server*
|
||||||
- name: Upload Assets jar binaries
|
|
||||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
|
||||||
with:
|
|
||||||
path: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
|
||||||
name: Stirling-PDF${{ matrix.file_suffix }}.jar
|
|
||||||
overwrite: true
|
|
||||||
retention-days: 1
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Upload jar binaries to release
|
|
||||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
|
||||||
with:
|
|
||||||
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
|
||||||
|
|||||||
8
.github/workflows/scorecards.yml
vendored
8
.github/workflows/scorecards.yml
vendored
@@ -10,7 +10,7 @@ on:
|
|||||||
# To guarantee Maintained check is occasionally updated. See
|
# To guarantee Maintained check is occasionally updated. See
|
||||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '20 7 * * 2'
|
- cron: "20 7 * * 2"
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
@@ -74,6 +74,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -37,4 +37,4 @@ jobs:
|
|||||||
only-issue-labels: "more-info-needed"
|
only-issue-labels: "more-info-needed"
|
||||||
days-before-pr-stale: -1 # Prevents PRs from being marked as stale
|
days-before-pr-stale: -1 # Prevents PRs from being marked as stale
|
||||||
days-before-pr-close: -1 # Prevents PRs from being closed
|
days-before-pr-close: -1 # Prevents PRs from being closed
|
||||||
start-date: '2024-07-06T00:00:00Z' # ISO 8601 Format
|
start-date: "2024-07-06T00:00:00Z" # ISO 8601 Format
|
||||||
|
|||||||
2
.github/workflows/swagger.yml
vendored
2
.github/workflows/swagger.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
148
.github/workflows/sync_files.yml
vendored
148
.github/workflows/sync_files.yml
vendored
@@ -1,62 +1,170 @@
|
|||||||
name: Sync Files
|
name: Sync Files
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- "build.gradle"
|
- "build.gradle"
|
||||||
|
- "README.md"
|
||||||
|
- "gradle/verification-keyring.keys"
|
||||||
|
- "gradle/verification-metadata.xml"
|
||||||
- "src/main/resources/messages_*.properties"
|
- "src/main/resources/messages_*.properties"
|
||||||
|
- "src/main/resources/static/3rdPartyLicenses.json"
|
||||||
- "scripts/ignore_translation.toml"
|
- "scripts/ignore_translation.toml"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync-readme:
|
read_bot_entries:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
outputs:
|
||||||
contents: write
|
userName: ${{ steps.get-user-id.outputs.user_name }}
|
||||||
pull-requests: write
|
userEmail: ${{ steps.get-user-id.outputs.user_email }}
|
||||||
|
committer: ${{ steps.committer.outputs.committer }}
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Generate GitHub App Token
|
||||||
|
id: generate-token
|
||||||
|
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.GH_APP_ID }}
|
||||||
|
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Get GitHub App User ID
|
||||||
|
id: get-user-id
|
||||||
|
run: |
|
||||||
|
USER_NAME="${{ steps.generate-token.outputs.app-slug }}[bot]"
|
||||||
|
USER_ID=$(gh api "/users/$USER_NAME" --jq .id)
|
||||||
|
USER_EMAIL="$USER_ID+$USER_NAME@users.noreply.github.com"
|
||||||
|
echo "user_name=$USER_NAME" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "user_email=$USER_EMAIL" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "user-id=$USER_ID" >> "$GITHUB_OUTPUT"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
|
- id: committer
|
||||||
|
run: |
|
||||||
|
COMMITTER="${{ steps.get-user-id.outputs.user_name }} <${{ steps.get-user-id.outputs.user_email }}>"
|
||||||
|
echo "committer=$COMMITTER" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
sync-files:
|
||||||
|
needs: ["read_bot_entries"]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Generate GitHub App Token
|
||||||
|
id: generate-token
|
||||||
|
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||||
|
with:
|
||||||
|
app-id: ${{ vars.GH_APP_ID }}
|
||||||
|
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
- name: Install dependencies
|
cache: 'pip' # caching pip dependencies
|
||||||
run: pip install tomlkit
|
|
||||||
- name: Sync README
|
- name: Sync translation property files
|
||||||
run: python scripts/counter_translation.py
|
run: |
|
||||||
|
python .github/scripts/check_language_properties.py --reference-file "src/main/resources/messages_en_GB.properties" --branch main
|
||||||
|
|
||||||
- name: Set up git config
|
- name: Set up git config
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name "github-actions[bot]"
|
git config --global user.name ${{ needs.read_bot_entries.outputs.userName }}
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
git config --global user.email ${{ needs.read_bot_entries.outputs.userEmail }}
|
||||||
|
|
||||||
- name: Run git add
|
- name: Run git add
|
||||||
run: |
|
run: |
|
||||||
git add .
|
git add src/main/resources/messages_*.properties
|
||||||
git diff --staged --quiet || git commit -m ":memo: Sync README
|
git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "no changes"
|
||||||
> Made via sync_files.yml" || echo "no changes"
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pip install --require-hashes -r ./.github/scripts/requirements_sync_readme.txt
|
||||||
|
|
||||||
|
- name: Sync README.md
|
||||||
|
run: |
|
||||||
|
python scripts/counter_translation.py
|
||||||
|
|
||||||
|
- name: Run git add
|
||||||
|
run: |
|
||||||
|
git add README.md
|
||||||
|
git diff --staged --quiet || git commit -m ":memo: Sync README.md" || echo "no changes"
|
||||||
|
|
||||||
|
- name: Generate verification metadata with signatures and checksums
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
if [ -f ./gradle/verification-metadata.xml ]; then
|
||||||
|
rm ./gradle/verification-metadata.xml
|
||||||
|
fi
|
||||||
|
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 help
|
||||||
|
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help
|
||||||
|
./gradlew clean build
|
||||||
|
|
||||||
|
- name: Run git add
|
||||||
|
run: |
|
||||||
|
git add gradle/verification-keyring.keys
|
||||||
|
git add gradle/verification-metadata.xml
|
||||||
|
git diff --staged --quiet || git commit -m ":memo: Generate verification metadata with signatures and checksums" || echo "no changes"
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
commit-message: Update files
|
commit-message: Update files
|
||||||
committer: GitHub Action <action@github.com>
|
committer: ${{ needs.read_bot_entries.outputs.committer }}
|
||||||
author: GitHub Action <action@github.com>
|
author: ${{ needs.read_bot_entries.outputs.committer }}
|
||||||
signoff: true
|
signoff: true
|
||||||
branch: sync_readme
|
branch: sync_readme
|
||||||
title: ":memo: Update README: Translation Progress Table"
|
title: ":globe_with_meridians: Sync Translations + Update README Progress Table + Update Verification Metadata"
|
||||||
body: |
|
body: |
|
||||||
Auto-generated by [create-pull-request][1]
|
### Description of Changes
|
||||||
|
|
||||||
|
This Pull Request was automatically generated to synchronize updates to translation files, verification metadata, and documentation. Below are the details of the changes made:
|
||||||
|
|
||||||
|
#### **1. Synchronization of Translation Files**
|
||||||
|
- Updated translation files (`messages_*.properties`) to reflect changes in the reference file `messages_en_GB.properties`.
|
||||||
|
- Ensured consistency and synchronization across all supported language files.
|
||||||
|
- Highlighted any missing or incomplete translations.
|
||||||
|
|
||||||
|
#### **2. Update README.md**
|
||||||
|
- Generated the translation progress table in `README.md`.
|
||||||
|
- Added a summary of the current translation status for all supported languages.
|
||||||
|
- Included up-to-date statistics on translation coverage.
|
||||||
|
|
||||||
|
#### **3. Verification Metadata Updates**
|
||||||
|
- Generated or refreshed the `verification-keyring.keys` and `verification-metadata.xml` files.
|
||||||
|
- Included the latest dependency signatures and checksums to enhance the build's integrity.
|
||||||
|
|
||||||
|
#### **Why these changes are necessary**
|
||||||
|
- Keeps translation files aligned with the latest reference updates.
|
||||||
|
- Ensures the documentation reflects the current translation progress.
|
||||||
|
- Strengthens dependency verification for a more secure build process.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Auto-generated by [create-pull-request][1].
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
[1]: https://github.com/peter-evans/create-pull-request
|
||||||
draft: false
|
draft: false
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
labels: Documentation,Translation,github-actions
|
labels: github-actions
|
||||||
sign-commits: true
|
sign-commits: true
|
||||||
|
add-paths: |
|
||||||
|
README.md
|
||||||
|
src/main/resources/messages_*.properties
|
||||||
|
gradle/verification-keyring.keys
|
||||||
|
gradle/verification-metadata.xml
|
||||||
|
|||||||
154
.github/workflows/testdriver.yml
vendored
Normal file
154
.github/workflows/testdriver.yml
vendored
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
name: UI test with TestDriverAI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["master", "UITest", "testdriver"]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
|
with:
|
||||||
|
java-version: '17'
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew clean build
|
||||||
|
env:
|
||||||
|
DOCKER_ENABLE_SECURITY: false
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||||
|
|
||||||
|
- name: Get version number
|
||||||
|
id: versionNumber
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||||
|
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_API }}
|
||||||
|
|
||||||
|
- name: Build and push test image
|
||||||
|
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:test-${{ github.sha }}
|
||||||
|
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
|
platforms: linux/amd64
|
||||||
|
|
||||||
|
- name: Set up SSH
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh/
|
||||||
|
echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
|
||||||
|
sudo chmod 600 ../private.key
|
||||||
|
|
||||||
|
- name: Deploy to VPS
|
||||||
|
run: |
|
||||||
|
cat > docker-compose.yml << EOF
|
||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
stirling-pdf:
|
||||||
|
container_name: stirling-pdf-test-${{ github.sha }}
|
||||||
|
image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:test-${{ github.sha }}
|
||||||
|
ports:
|
||||||
|
- "1337:8080"
|
||||||
|
volumes:
|
||||||
|
- /stirling/test-${{ github.sha }}/data:/usr/share/tessdata:rw
|
||||||
|
- /stirling/test-${{ github.sha }}/config:/configs:rw
|
||||||
|
- /stirling/test-${{ github.sha }}/logs:/logs:rw
|
||||||
|
environment:
|
||||||
|
DOCKER_ENABLE_SECURITY: "false"
|
||||||
|
SECURITY_ENABLELOGIN: "false"
|
||||||
|
SYSTEM_DEFAULTLOCALE: en-GB
|
||||||
|
UI_APPNAME: "Stirling-PDF Test"
|
||||||
|
UI_HOMEDESCRIPTION: "Test Deployment"
|
||||||
|
UI_APPNAMENAVBAR: "Test"
|
||||||
|
SYSTEM_MAXFILESIZE: "100"
|
||||||
|
METRICS_ENABLED: "true"
|
||||||
|
SYSTEM_GOOGLEVISIBILITY: "false"
|
||||||
|
SYSTEM_ENABLEANALYTICS: "false"
|
||||||
|
restart: on-failure:5
|
||||||
|
EOF
|
||||||
|
|
||||||
|
scp -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null docker-compose.yml ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }}:/tmp/docker-compose.yml
|
||||||
|
|
||||||
|
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << EOF
|
||||||
|
mkdir -p /stirling/test-${{ github.sha }}/{data,config,logs}
|
||||||
|
mv /tmp/docker-compose.yml /stirling/test-${{ github.sha }}/docker-compose.yml
|
||||||
|
cd /stirling/test-${{ github.sha }}
|
||||||
|
docker-compose pull
|
||||||
|
docker-compose up -d
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test:
|
||||||
|
needs: deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Run TestDriver.ai
|
||||||
|
uses: testdriverai/action@47e87c5d50beeeb3da624b2d9b5c1391269d6d22 #1.0.0
|
||||||
|
with:
|
||||||
|
key: ${{secrets.TESTDRIVER_API_KEY}}
|
||||||
|
prerun: |
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
npm install dashcam-chrome --save
|
||||||
|
Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--load-extension=$(pwd)/node_modules/dashcam-chrome/build", "http://${{ secrets.VPS_HOST }}:1337"
|
||||||
|
Start-Sleep -Seconds 20
|
||||||
|
prompt: |
|
||||||
|
1. /run testing/testdriver/test.yml
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
FORCE_COLOR: "3"
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
needs: [deploy, test]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: always()
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Set up SSH
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh/
|
||||||
|
echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
|
||||||
|
sudo chmod 600 ../private.key
|
||||||
|
|
||||||
|
- name: Cleanup deployment
|
||||||
|
run: |
|
||||||
|
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << EOF
|
||||||
|
cd /stirling/test-${{ github.sha }}
|
||||||
|
docker-compose down
|
||||||
|
cd /stirling
|
||||||
|
rm -rf test-${{ github.sha }}
|
||||||
|
EOF
|
||||||
72
.github/workflows/update-translations.yml
vendored
72
.github/workflows/update-translations.yml
vendored
@@ -1,72 +0,0 @@
|
|||||||
name: Update Translations
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
paths:
|
|
||||||
- "src/main/resources/messages_en_GB.properties"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-translations-main:
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
|
|
||||||
- name: Run Python script to check files
|
|
||||||
id: run-check
|
|
||||||
run: |
|
|
||||||
echo "Running Python script to check files..."
|
|
||||||
python .github/scripts/check_language_properties.py \
|
|
||||||
--reference-file src/main/resources/messages_en_GB.properties \
|
|
||||||
--branch main
|
|
||||||
|
|
||||||
- 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: |
|
|
||||||
git add src/main/resources/messages_*.properties
|
|
||||||
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
|
||||||
id: cpr
|
|
||||||
if: env.CHANGES_DETECTED == 'true'
|
|
||||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commit-message: "Update translation files"
|
|
||||||
committer: GitHub Action <action@github.com>
|
|
||||||
author: GitHub Action <action@github.com>
|
|
||||||
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]
|
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
|
||||||
draft: false
|
|
||||||
delete-branch: true
|
|
||||||
labels: Translation,github-actions
|
|
||||||
sign-commits: true
|
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -14,12 +14,18 @@ local.properties
|
|||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
version.properties
|
version.properties
|
||||||
|
|
||||||
|
#### Stirling-PDF Files ###
|
||||||
pipeline/watchedFolders/
|
pipeline/watchedFolders/
|
||||||
pipeline/finishedFolders/
|
pipeline/finishedFolders/
|
||||||
#### Stirling-PDF Files ###
|
|
||||||
customFiles/
|
customFiles/
|
||||||
configs/
|
configs/
|
||||||
watchedFolders/
|
watchedFolders/
|
||||||
|
clientWebUI/
|
||||||
|
!cucumber/
|
||||||
|
!cucumber/exampleFiles/
|
||||||
|
!cucumber/exampleFiles/example_html.zip
|
||||||
|
exampleYmlFiles/stirling/
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
@@ -111,6 +117,7 @@ watchedFolders/
|
|||||||
*.war
|
*.war
|
||||||
*.nar
|
*.nar
|
||||||
*.ear
|
*.ear
|
||||||
|
*.zip
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
*.db
|
*.db
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ repos:
|
|||||||
args:
|
args:
|
||||||
- --fix
|
- --fix
|
||||||
- --line-length=127
|
- --line-length=127
|
||||||
files: ^((.github/scripts|scripts)/.+)?[^/]+\.py$
|
files: ^((\.github/scripts|scripts)/.+)?[^/]+\.py$
|
||||||
exclude: (split_photos.py)
|
exclude: (split_photos.py)
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
files: ^((.github/scripts|scripts)/.+)?[^/]+\.py$
|
files: ^((\.github/scripts|scripts)/.+)?[^/]+\.py$
|
||||||
exclude: (split_photos.py)
|
exclude: (split_photos.py)
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.3.0
|
rev: v2.3.0
|
||||||
@@ -19,39 +19,18 @@ repos:
|
|||||||
- --ignore-words-list=
|
- --ignore-words-list=
|
||||||
- --skip="./.*,*.csv,*.json,*.ambr"
|
- --skip="./.*,*.csv,*.json,*.ambr"
|
||||||
- --quiet-level=2
|
- --quiet-level=2
|
||||||
files: \.(properties|html|css|js|py|md)$
|
files: \.(html|css|js|py|md)$
|
||||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile)
|
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
|
||||||
- repo: https://github.com/gitleaks/gitleaks
|
- repo: https://github.com/gitleaks/gitleaks
|
||||||
rev: v8.22.0
|
rev: v8.22.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: gitleaks
|
- id: gitleaks
|
||||||
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
|
||||||
rev: 3.0.0
|
|
||||||
hooks:
|
|
||||||
- id: shellcheck
|
|
||||||
files: ^.*(\.bash|\.sh|\.ksh|\.zsh)$
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v5.0.0
|
rev: v5.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
files: ^.*(\.js|\.java|\.py|\.yml)$
|
files: ^.*(\.js|\.java|\.py|\.yml)$
|
||||||
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
|
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js|\.github/workflows/.*$)
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
files: ^.*(\.js|\.java|\.py|\.yml)$
|
files: ^.*(\.js|\.java|\.py|\.yml)$
|
||||||
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
|
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js|\.github/workflows/.*$)
|
||||||
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: check-duplicate-properties-keys
|
|
||||||
name: Check Duplicate Properties Keys
|
|
||||||
entry: python .github/scripts/check_duplicates.py
|
|
||||||
language: python
|
|
||||||
files: ^(src)/.+\.properties$
|
|
||||||
- id: check-html-tabs
|
|
||||||
name: Check HTML for tabs
|
|
||||||
description: Ensures HTML/CSS/JS files do not contain tab characters
|
|
||||||
# args: ["--replace_with= "]
|
|
||||||
entry: python .github/scripts/check_tabulator.py
|
|
||||||
language: python
|
|
||||||
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
|
|
||||||
files: ^.*(\.html|\.css|\.js)$
|
|
||||||
|
|||||||
@@ -575,3 +575,42 @@ In your Thymeleaf templates, use the `#{key}` syntax to reference the new transl
|
|||||||
```
|
```
|
||||||
|
|
||||||
Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization.
|
Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization.
|
||||||
|
|
||||||
|
|
||||||
|
## Managing Dependencies
|
||||||
|
|
||||||
|
When adding new dependencies or updating existing ones in Stirling-PDF, follow these steps to ensure proper verification and security:
|
||||||
|
|
||||||
|
1. Update the dependency in `build.gradle`:
|
||||||
|
```groovy
|
||||||
|
dependencies {
|
||||||
|
// Add or update your dependency
|
||||||
|
implementation "com.example:new-library:1.2.3"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Generate new verification metadata and keys:
|
||||||
|
```bash
|
||||||
|
# Generate verification metadata with signatures and checksums
|
||||||
|
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp
|
||||||
|
|
||||||
|
# Export the .keys file
|
||||||
|
./gradlew --export-keys
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Files to commit:
|
||||||
|
- `build.gradle` - Your dependency changes
|
||||||
|
- `gradle/verification-metadata.xml` - Contains verification rules and checksums
|
||||||
|
- `gradle/verification-keyring.keys` - Contains PGP keys in text format
|
||||||
|
|
||||||
|
4. Verify the build works with the new verification:
|
||||||
|
```bash
|
||||||
|
./gradlew build
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Before committing, check:
|
||||||
|
- Verify any new BOM files are properly handled in verification metadata
|
||||||
|
- Review the changes in `verification-metadata.xml` to ensure they match your dependency updates
|
||||||
|
|
||||||
|
This ensures dependencies are properly verified and secure while maintaining transparency in the repository.
|
||||||
|
|
||||||
|
|||||||
10
Dockerfile
10
Dockerfile
@@ -1,5 +1,5 @@
|
|||||||
# Main stage
|
# Main stage
|
||||||
FROM alpine:3.20.3
|
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||||
|
|
||||||
# Copy necessary files
|
# Copy necessary files
|
||||||
COPY scripts /scripts
|
COPY scripts /scripts
|
||||||
@@ -25,7 +25,13 @@ LABEL org.opencontainers.image.keywords="PDF, manipulation, merge, split, conver
|
|||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
|
||||||
|
-XX:MaxRAMPercentage=75 \
|
||||||
|
-XX:InitiatingHeapOccupancyPercent=20 \
|
||||||
|
-XX:+G1PeriodicGCInvokesConcurrent \
|
||||||
|
-XX:G1PeriodicGCInterval=10000 \
|
||||||
|
-XX:+UseStringDeduplication \
|
||||||
|
-XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
PUID=1000 \
|
PUID=1000 \
|
||||||
PGID=1000 \
|
PGID=1000 \
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ RUN DOCKER_ENABLE_SECURITY=true \
|
|||||||
./gradlew clean build
|
./gradlew clean build
|
||||||
|
|
||||||
# Main stage
|
# Main stage
|
||||||
FROM alpine:3.20.3
|
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||||
|
|
||||||
# Copy necessary files
|
# Copy necessary files
|
||||||
COPY scripts /scripts
|
COPY scripts /scripts
|
||||||
@@ -25,7 +25,13 @@ ARG VERSION_TAG
|
|||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
|
||||||
|
-XX:MaxRAMPercentage=75 \
|
||||||
|
-XX:InitiatingHeapOccupancyPercent=20 \
|
||||||
|
-XX:+G1PeriodicGCInvokesConcurrent \
|
||||||
|
-XX:G1PeriodicGCInterval=10000 \
|
||||||
|
-XX:+UseStringDeduplication \
|
||||||
|
-XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
PUID=1000 \
|
PUID=1000 \
|
||||||
PGID=1000 \
|
PGID=1000 \
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# use alpine
|
# use alpine
|
||||||
FROM alpine:3.21.0@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45
|
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
@@ -7,7 +7,13 @@ ARG VERSION_TAG
|
|||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
|
||||||
|
-XX:MaxRAMPercentage=75 \
|
||||||
|
-XX:InitiatingHeapOccupancyPercent=20 \
|
||||||
|
-XX:+G1PeriodicGCInvokesConcurrent \
|
||||||
|
-XX:G1PeriodicGCInterval=10000 \
|
||||||
|
-XX:+UseStringDeduplication \
|
||||||
|
-XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
PUID=1000 \
|
PUID=1000 \
|
||||||
PGID=1000 \
|
PGID=1000 \
|
||||||
UMASK=022
|
UMASK=022
|
||||||
|
|||||||
@@ -60,3 +60,13 @@ ignore = [
|
|||||||
- After adding the new tags to `messages_en_GB.properties`, add and translate them in the respective language file (e.g., `messages_pl_PL.properties`).
|
- After adding the new tags to `messages_en_GB.properties`, add and translate them in the respective language file (e.g., `messages_pl_PL.properties`).
|
||||||
|
|
||||||
Make sure to place the entry under the correct language section. This helps maintain the accuracy of translation progress statistics and ensures that the translation tool or scripts do not misinterpret the completion rate.
|
Make sure to place the entry under the correct language section. This helps maintain the accuracy of translation progress statistics and ensures that the translation tool or scripts do not misinterpret the completion rate.
|
||||||
|
|
||||||
|
### Use this code to perform a local check
|
||||||
|
|
||||||
|
#### Windows command
|
||||||
|
|
||||||
|
```ps
|
||||||
|
python .github/scripts/check_language_properties.py --reference-file src\main\resources\messages_en_GB.properties --branch "" --files src\main\resources\messages_pl_PL.properties
|
||||||
|
|
||||||
|
python .github/scripts/check_language_properties.py --reference-file src\main\resources\messages_en_GB.properties --branch "" --check-file src\main\resources\messages_pl_PL.properties
|
||||||
|
```
|
||||||
|
|||||||
53
README.md
53
README.md
@@ -113,46 +113,47 @@ Visit our comprehensive documentation at [docs.stirlingpdf.com](https://docs.sti
|
|||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
Stirling-PDF currently supports 38 languages!
|
Stirling-PDF currently supports 39 languages!
|
||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
| Dutch (Nederlands) (nl_NL) |  |
|
| Dutch (Nederlands) (nl_NL) |  |
|
||||||
| English (English) (en_GB) |  |
|
| English (English) (en_GB) |  |
|
||||||
| English (US) (en_US) |  |
|
| English (US) (en_US) |  |
|
||||||
| French (Français) (fr_FR) |  |
|
| French (Français) (fr_FR) |  |
|
||||||
| German (Deutsch) (de_DE) |  |
|
| German (Deutsch) (de_DE) |  |
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
| Greek (Ελληνικά) (el_GR) |  |
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
| Hindi (हिंदी) (hi_IN) |  |
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||||
| Irish (Gaeilge) (ga_IE) |  |
|
| Irish (Gaeilge) (ga_IE) |  |
|
||||||
| Italian (Italiano) (it_IT) |  |
|
| Italian (Italiano) (it_IT) |  |
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
| Japanese (日本語) (ja_JP) |  |
|
||||||
| Korean (한국어) (ko_KR) |  |
|
| Korean (한국어) (ko_KR) |  |
|
||||||
| Norwegian (Norsk) (no_NB) |  |
|
| Norwegian (Norsk) (no_NB) |  |
|
||||||
| Persian (فارسی) (fa_IR) |  |
|
| Persian (فارسی) (fa_IR) |  |
|
||||||
| Polish (Polski) (pl_PL) |  |
|
| Polish (Polski) (pl_PL) |  |
|
||||||
| Portuguese (Português) (pt_PT) |  |
|
| Portuguese (Português) (pt_PT) |  |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||||
| Romanian (Română) (ro_RO) |  |
|
| Romanian (Română) (ro_RO) |  |
|
||||||
| Russian (Русский) (ru_RU) |  |
|
| Russian (Русский) (ru_RU) |  |
|
||||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Slovenian (Slovenščina) (sl_SI) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||||
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
|||||||
45
USERS.md
Normal file
45
USERS.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Who is using Stirling-PDF?
|
||||||
|
|
||||||
|
Understanding the diverse applications of Stirling-PDF can be an invaluable resource for collaboration and learning. This page provides a directory of users and use cases. If you are using Stirling-PDF, consider sharing your experiences to help others and foster a community of best practices.
|
||||||
|
|
||||||
|
## Adding Yourself as a User
|
||||||
|
|
||||||
|
If you're using Stirling-PDF or have integrated it into your platform or workflow, please consider contributing to this list by describing your use case. You can do this by opening a pull request to this file and adding your details in the format below:
|
||||||
|
|
||||||
|
- **N**: Name of the organization or individual.
|
||||||
|
- **D**: A brief description of your usage.
|
||||||
|
- **U**: Specific features or capabilities utilized.
|
||||||
|
- **L**: Optional link for further information (e.g., website, blog post).
|
||||||
|
- **Q**: Contact information for sharing insights (optional).
|
||||||
|
|
||||||
|
Example entry:
|
||||||
|
|
||||||
|
```
|
||||||
|
* N: Example Corp
|
||||||
|
D: Using Stirling-PDF for automated document processing in our SaaS platform focusing on compression.
|
||||||
|
U: OCR, merging PDFs, metadata editing, encryption, compression.
|
||||||
|
L: https://example.com/stirling-pdf
|
||||||
|
Q: @example-user on Discord/email
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requirements for Listing
|
||||||
|
|
||||||
|
- You must represent the entity you're listing and ensure the details are accurate.
|
||||||
|
- Trial deployments are welcome if they represent a realistic evaluation of Stirling-PDF in action.
|
||||||
|
- Community contributions, including home-lab setups or non-commercial uses, are encouraged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Users (Alphabetically)
|
||||||
|
|
||||||
|
* N:
|
||||||
|
D:
|
||||||
|
U:
|
||||||
|
L:
|
||||||
|
|
||||||
|
* N:
|
||||||
|
D:
|
||||||
|
U:
|
||||||
|
L:
|
||||||
111
build.gradle
111
build.gradle
@@ -5,14 +5,12 @@ plugins {
|
|||||||
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
||||||
id "io.swagger.swaggerhub" version "1.3.2"
|
id "io.swagger.swaggerhub" version "1.3.2"
|
||||||
id "edu.sc.seis.launch4j" version "3.0.6"
|
id "edu.sc.seis.launch4j" version "3.0.6"
|
||||||
id "com.diffplug.spotless" version "6.25.0"
|
id "com.diffplug.spotless" version "7.0.2"
|
||||||
id "com.github.jk1.dependency-license-report" version "2.9"
|
id "com.github.jk1.dependency-license-report" version "2.9"
|
||||||
//id "nebula.lint" version "19.0.3"
|
//id "nebula.lint" version "19.0.3"
|
||||||
id("org.panteleyev.jpackageplugin") version "1.6.0"
|
id("org.panteleyev.jpackageplugin") version "1.6.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import com.github.jk1.license.render.*
|
import com.github.jk1.license.render.*
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
@@ -21,13 +19,13 @@ ext {
|
|||||||
logbackVersion = "1.5.7"
|
logbackVersion = "1.5.7"
|
||||||
imageioVersion = "3.12.0"
|
imageioVersion = "3.12.0"
|
||||||
lombokVersion = "1.18.36"
|
lombokVersion = "1.18.36"
|
||||||
bouncycastleVersion = "1.79"
|
bouncycastleVersion = "1.80"
|
||||||
springSecuritySamlVersion = "6.4.2"
|
springSecuritySamlVersion = "6.4.2"
|
||||||
openSamlVersion = "4.3.2"
|
openSamlVersion = "4.3.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.36.6"
|
version = "0.39.0"
|
||||||
|
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -37,9 +35,9 @@ java {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url "https://jitpack.io" }
|
maven { url = "https://jitpack.io" }
|
||||||
maven { url "https://build.shibboleth.net/maven/releases" }
|
maven { url = "https://build.shibboleth.net/maven/releases" }
|
||||||
maven { url "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
|
maven { url = "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +66,7 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
|
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
|
||||||
exclude "stirling/software/SPDF/UI/impl/**"
|
exclude "stirling/software/SPDF/UI/impl/**"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -113,18 +111,15 @@ def getMacVersion(String version) {
|
|||||||
|
|
||||||
jpackage {
|
jpackage {
|
||||||
input = "build/libs"
|
input = "build/libs"
|
||||||
|
destination = "${projectDir}/build/jpackage"
|
||||||
|
mainJar = "Stirling-PDF-${project.version}.jar"
|
||||||
appName = "Stirling-PDF"
|
appName = "Stirling-PDF"
|
||||||
appVersion = project.version
|
appVersion = project.version
|
||||||
vendor = "Stirling-Software"
|
vendor = "Stirling-Software"
|
||||||
appDescription = "Stirling PDF - Your Local PDF Editor"
|
appDescription = "Stirling PDF - Your Local PDF Editor"
|
||||||
|
|
||||||
mainJar = "Stirling-PDF-${project.version}.jar"
|
|
||||||
mainClass = "org.springframework.boot.loader.launch.JarLauncher"
|
|
||||||
|
|
||||||
icon = "src/main/resources/static/favicon.ico"
|
icon = "src/main/resources/static/favicon.ico"
|
||||||
|
verbose = true
|
||||||
|
// mainClass = "org.springframework.boot.loader.launch.JarLauncher"
|
||||||
|
|
||||||
// JVM Options
|
// JVM Options
|
||||||
javaOptions = [
|
javaOptions = [
|
||||||
@@ -132,23 +127,21 @@ jpackage {
|
|||||||
"-DSTIRLING_PDF_DESKTOP_UI=true",
|
"-DSTIRLING_PDF_DESKTOP_UI=true",
|
||||||
"-Djava.awt.headless=false",
|
"-Djava.awt.headless=false",
|
||||||
"-Dapple.awt.UIElement=true",
|
"-Dapple.awt.UIElement=true",
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
|
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||||
"--add-opens", "java.desktop/java.awt.event=ALL-UNNAMED",
|
"--add-opens=java.desktop/java.awt.event=ALL-UNNAMED",
|
||||||
"--add-opens", "java.desktop/sun.awt=ALL-UNNAMED"
|
"--add-opens=java.desktop/sun.awt=ALL-UNNAMED",
|
||||||
|
"--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED",
|
||||||
|
"--add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED",
|
||||||
|
"--add-opens=java.desktop/sun.lwawt=ALL-UNNAMED",
|
||||||
|
"--add-opens=java.desktop/sun.lwawt.macosx=ALL-UNNAMED",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
verbose = true
|
|
||||||
|
|
||||||
destination = "${projectDir}/build/jpackage"
|
|
||||||
|
|
||||||
// Windows-specific configuration
|
// Windows-specific configuration
|
||||||
windows {
|
windows {
|
||||||
launcherAsService = false
|
launcherAsService = false
|
||||||
appVersion = project.version
|
appVersion = project.version
|
||||||
|
|
||||||
winConsole = false
|
winConsole = false
|
||||||
winMenu = true // Creates start menu entry
|
winMenu = true // Creates start menu entry
|
||||||
winShortcut = true // Creates desktop shortcut
|
winShortcut = true // Creates desktop shortcut
|
||||||
winShortcutPrompt = true // Lets user choose whether to create shortcuts
|
winShortcutPrompt = true // Lets user choose whether to create shortcuts
|
||||||
@@ -164,7 +157,7 @@ jpackage {
|
|||||||
|
|
||||||
// macOS-specific configuration
|
// macOS-specific configuration
|
||||||
mac {
|
mac {
|
||||||
appVersion = getMacVersion(project.version.toString())
|
appVersion = getMacVersion(project.version.toString())
|
||||||
icon = "src/main/resources/static/favicon.icns"
|
icon = "src/main/resources/static/favicon.icns"
|
||||||
type = "dmg"
|
type = "dmg"
|
||||||
macPackageIdentifier = "com.stirling.software.pdf"
|
macPackageIdentifier = "com.stirling.software.pdf"
|
||||||
@@ -188,7 +181,7 @@ jpackage {
|
|||||||
|
|
||||||
// Linux-specific configuration
|
// Linux-specific configuration
|
||||||
linux {
|
linux {
|
||||||
appVersion = project.version
|
appVersion = project.version
|
||||||
icon = "src/main/resources/static/favicon.png"
|
icon = "src/main/resources/static/favicon.png"
|
||||||
type = "deb" // Can also use "rpm" for Red Hat-based systems
|
type = "deb" // Can also use "rpm" for Red Hat-based systems
|
||||||
|
|
||||||
@@ -236,9 +229,9 @@ launch4j {
|
|||||||
outfile="Stirling-PDF.exe"
|
outfile="Stirling-PDF.exe"
|
||||||
|
|
||||||
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
|
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
|
||||||
headerType = "gui"
|
headerType = "gui"
|
||||||
} else {
|
} else {
|
||||||
headerType = "console"
|
headerType = "console"
|
||||||
}
|
}
|
||||||
jarTask = tasks.bootJar
|
jarTask = tasks.bootJar
|
||||||
|
|
||||||
@@ -246,13 +239,11 @@ launch4j {
|
|||||||
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
|
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
|
||||||
|
|
||||||
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
|
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
|
||||||
variables=["BROWSER_OPEN=true", "STIRLING_PDF_DESKTOP_UI=true"]
|
variables=["BROWSER_OPEN=true", "STIRLING_PDF_DESKTOP_UI=true"]
|
||||||
} else {
|
} else {
|
||||||
variables=["BROWSER_OPEN=true"]
|
variables=["BROWSER_OPEN=true"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
jreMinVersion="17"
|
jreMinVersion="17"
|
||||||
|
|
||||||
mutexName="Stirling-PDF"
|
mutexName="Stirling-PDF"
|
||||||
@@ -274,7 +265,7 @@ spotless {
|
|||||||
importOrder("java", "javax", "org", "com", "net", "io")
|
importOrder("java", "javax", "org", "com", "net", "io")
|
||||||
toggleOffOn()
|
toggleOffOn()
|
||||||
trimTrailingWhitespace()
|
trimTrailingWhitespace()
|
||||||
indentWithSpaces()
|
leadingTabsToSpaces()
|
||||||
endWithNewline()
|
endWithNewline()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,14 +284,14 @@ configurations.all {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
|
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
|
||||||
implementation "me.friwi:jcefmaven:127.3.1"
|
implementation "me.friwi:jcefmaven:127.3.1"
|
||||||
implementation "org.openjfx:javafx-controls:21"
|
implementation "org.openjfx:javafx-controls:21"
|
||||||
implementation "org.openjfx:javafx-swing:21"
|
implementation "org.openjfx:javafx-swing:21"
|
||||||
}
|
}
|
||||||
|
|
||||||
//security updates
|
//security updates
|
||||||
implementation "org.springframework:spring-webmvc:6.2.1"
|
implementation "org.springframework:spring-webmvc:6.2.2"
|
||||||
|
|
||||||
implementation("io.github.pixee:java-security-toolkit:1.2.1")
|
implementation("io.github.pixee:java-security-toolkit:1.2.1")
|
||||||
|
|
||||||
@@ -322,10 +313,10 @@ dependencies {
|
|||||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
||||||
|
|
||||||
implementation "org.springframework.session:spring-session-core:$springBootVersion"
|
implementation "org.springframework.session:spring-session-core:$springBootVersion"
|
||||||
implementation "org.springframework:spring-jdbc:6.2.1"
|
implementation "org.springframework:spring-jdbc:6.2.2"
|
||||||
|
|
||||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
||||||
// Don't upgrade h2database
|
// Don't upgrade h2database
|
||||||
runtimeOnly "com.h2database:h2:2.3.232"
|
runtimeOnly "com.h2database:h2:2.3.232"
|
||||||
runtimeOnly "org.postgresql:postgresql:42.7.4"
|
runtimeOnly "org.postgresql:postgresql:42.7.4"
|
||||||
@@ -335,7 +326,7 @@ dependencies {
|
|||||||
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
|
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
|
||||||
}
|
}
|
||||||
implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
|
implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
|
||||||
// implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion'
|
// implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion'
|
||||||
implementation 'com.coveo:saml-client:5.0.0'
|
implementation 'com.coveo:saml-client:5.0.0'
|
||||||
|
|
||||||
|
|
||||||
@@ -372,13 +363,15 @@ dependencies {
|
|||||||
//general PDF
|
//general PDF
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/com.opencsv/opencsv
|
// https://mvnrepository.com/artifact/com.opencsv/opencsv
|
||||||
implementation ("com.opencsv:opencsv:5.9") {
|
implementation ("com.opencsv:opencsv:5.10") {
|
||||||
exclude group: "commons-logging", module: "commons-logging"
|
exclude group: "commons-logging", module: "commons-logging"
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") {
|
implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") {
|
||||||
exclude group: "commons-logging", module: "commons-logging"
|
exclude group: "commons-logging", module: "commons-logging"
|
||||||
}
|
}
|
||||||
|
implementation "org.apache.pdfbox:preflight:$pdfboxVersion"
|
||||||
|
|
||||||
|
|
||||||
implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") {
|
implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") {
|
||||||
exclude group: "commons-logging", module: "commons-logging"
|
exclude group: "commons-logging", module: "commons-logging"
|
||||||
@@ -396,7 +389,7 @@ dependencies {
|
|||||||
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||||
implementation "io.micrometer:micrometer-core:1.14.2"
|
implementation "io.micrometer:micrometer-core:1.14.3"
|
||||||
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
||||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||||
implementation "org.commonmark:commonmark:0.24.0"
|
implementation "org.commonmark:commonmark:0.24.0"
|
||||||
@@ -405,6 +398,8 @@ dependencies {
|
|||||||
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
|
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
|
||||||
implementation "com.fathzer:javaluator:3.0.5"
|
implementation "com.fathzer:javaluator:3.0.5"
|
||||||
|
|
||||||
|
implementation 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8'
|
||||||
|
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
|
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
|
||||||
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||||
@@ -428,13 +423,13 @@ task writeVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
swaggerhubUpload {
|
swaggerhubUpload {
|
||||||
//dependsOn generateOpenApiDocs // Depends on your task generating Swagger docs
|
// dependsOn = generateOpenApiDocs // Depends on your task generating Swagger docs
|
||||||
api "Stirling-PDF" // The name of your API on SwaggerHub
|
api = "Stirling-PDF" // The name of your API on SwaggerHub
|
||||||
owner "Frooodle" // Your SwaggerHub username (or organization name)
|
owner = "Frooodle" // Your SwaggerHub username (or organization name)
|
||||||
version project.version // The version of your API
|
version = project.version // The version of your API
|
||||||
inputFile "./SwaggerDoc.json" // The path to your Swagger docs
|
inputFile = "./SwaggerDoc.json" // The path to your Swagger docs
|
||||||
token "${System.getenv("SWAGGERHUB_API_KEY")}" // Your SwaggerHub API key, passed as an environment variable
|
token = "${System.getenv("SWAGGERHUB_API_KEY")}" // Your SwaggerHub API key, passed as an environment variable
|
||||||
oas "3.0.0" // The version of the OpenAPI Specification you"re using
|
oas = "3.0.0" // The version of the OpenAPI Specification you"re using
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
stirling-pdf:
|
stirling-pdf:
|
||||||
container_name: Stirling-PDF-Security-Fat
|
container_name: Stirling-PDF-Security-Fat-with-login
|
||||||
image: stirlingtools/stirling-pdf:latest-fat
|
image: stirlingtools/stirling-pdf:latest-fat
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
7
gradle.properties
Normal file
7
gradle.properties
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Enables parallel execution of tasks, allowing multiple tasks to run simultaneously
|
||||||
|
org.gradle.parallel=true
|
||||||
|
|
||||||
|
# Enables build caching to reuse outputs from previous builds for faster execution
|
||||||
|
# org.gradle.caching=true
|
||||||
|
|
||||||
|
org.gradle.build-scan=true
|
||||||
6661
gradle/verification-keyring.keys
Normal file
6661
gradle/verification-keyring.keys
Normal file
File diff suppressed because it is too large
Load Diff
4553
gradle/verification-metadata.xml
Normal file
4553
gradle/verification-metadata.xml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
37
gradlew
vendored
37
gradlew
vendored
@@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -80,13 +82,11 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -133,22 +133,29 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -193,11 +200,15 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
# double quotes to make sure that they get re-expanded; and
|
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
|||||||
23
gradlew.bat
vendored
23
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@@ -26,6 +28,7 @@ if "%OS%"=="Windows_NT" setlocal
|
|||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -42,11 +45,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ def write_readme(progress_list: list[tuple[str, int]]) -> None:
|
|||||||
f"",
|
f"",
|
||||||
)
|
)
|
||||||
|
|
||||||
with open("README.md", "w", encoding="utf-8") as file:
|
with open("README.md", "w", encoding="utf-8", newline="\n") as file:
|
||||||
file.writelines(content)
|
file.writelines(content)
|
||||||
|
|
||||||
|
|
||||||
@@ -135,9 +135,10 @@ def compare_files(
|
|||||||
# elif "language.direction" in sort_ignore_translation[language]["missing"]:
|
# elif "language.direction" in sort_ignore_translation[language]["missing"]:
|
||||||
# sort_ignore_translation[language]["missing"].remove("language.direction")
|
# sort_ignore_translation[language]["missing"].remove("language.direction")
|
||||||
|
|
||||||
with open(default_file_path, encoding="utf-8") as default_file, open(
|
with (
|
||||||
file_path, encoding="utf-8"
|
open(default_file_path, encoding="utf-8") as default_file,
|
||||||
) as file:
|
open(file_path, encoding="utf-8") as file,
|
||||||
|
):
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
next(default_file)
|
next(default_file)
|
||||||
try:
|
try:
|
||||||
@@ -195,7 +196,7 @@ def compare_files(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
ignore_translation = convert_to_multiline(sort_ignore_translation)
|
ignore_translation = convert_to_multiline(sort_ignore_translation)
|
||||||
with open(ignore_translation_file, "w", encoding="utf-8") as file:
|
with open(ignore_translation_file, "w", encoding="utf-8", newline="\n") as file:
|
||||||
file.write(tomlkit.dumps(ignore_translation))
|
file.write(tomlkit.dumps(ignore_translation))
|
||||||
|
|
||||||
unique_data = list(set(result_list))
|
unique_data = list(set(result_list))
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ ignore = [
|
|||||||
[cs_CZ]
|
[cs_CZ]
|
||||||
ignore = [
|
ignore = [
|
||||||
'language.direction',
|
'language.direction',
|
||||||
'pipeline.header',
|
|
||||||
'text',
|
'text',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -50,6 +49,7 @@ ignore = [
|
|||||||
'pipeline.title',
|
'pipeline.title',
|
||||||
'pipelineOptions.pipelineHeader',
|
'pipelineOptions.pipelineHeader',
|
||||||
'pro',
|
'pro',
|
||||||
|
'redact.zoom',
|
||||||
'sponsor',
|
'sponsor',
|
||||||
'text',
|
'text',
|
||||||
'validateSignature.cert.bits',
|
'validateSignature.cert.bits',
|
||||||
@@ -210,6 +210,11 @@ ignore = [
|
|||||||
'watermark.type.1',
|
'watermark.type.1',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[sl_SI]
|
||||||
|
ignore = [
|
||||||
|
'language.direction',
|
||||||
|
]
|
||||||
|
|
||||||
[sr_LATN_RS]
|
[sr_LATN_RS]
|
||||||
ignore = [
|
ignore = [
|
||||||
'language.direction',
|
'language.direction',
|
||||||
|
|||||||
@@ -27,4 +27,9 @@ public class EEAppConfig {
|
|||||||
public boolean runningEnterpriseEdition() {
|
public boolean runningEnterpriseEdition() {
|
||||||
return licenseKeyChecker.getEnterpriseEnabledResult();
|
return licenseKeyChecker.getEnterpriseEnabledResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = "SSOAutoLogin")
|
||||||
|
public boolean ssoAutoLogin() {
|
||||||
|
return applicationProperties.getEnterpriseEdition().isSsoAutoLogin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public class KeygenLicenseVerifier {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
log.info(" validateLicenseResponse body: " + response.body());
|
log.debug(" validateLicenseResponse body: " + response.body());
|
||||||
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
||||||
if (response.statusCode() == 200) {
|
if (response.statusCode() == 200) {
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Convert", "url-to-pdf");
|
addEndpointToGroup("Convert", "url-to-pdf");
|
||||||
addEndpointToGroup("Convert", "markdown-to-pdf");
|
addEndpointToGroup("Convert", "markdown-to-pdf");
|
||||||
addEndpointToGroup("Convert", "pdf-to-csv");
|
addEndpointToGroup("Convert", "pdf-to-csv");
|
||||||
|
addEndpointToGroup("Convert", "pdf-to-markdown");
|
||||||
|
|
||||||
// Adding endpoints to "Security" group
|
// Adding endpoints to "Security" group
|
||||||
addEndpointToGroup("Security", "add-password");
|
addEndpointToGroup("Security", "add-password");
|
||||||
@@ -182,7 +183,6 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Python", "extract-image-scans");
|
addEndpointToGroup("Python", "extract-image-scans");
|
||||||
addEndpointToGroup("Python", "html-to-pdf");
|
addEndpointToGroup("Python", "html-to-pdf");
|
||||||
addEndpointToGroup("Python", "url-to-pdf");
|
addEndpointToGroup("Python", "url-to-pdf");
|
||||||
addEndpointToGroup("Python", "pdf-to-img");
|
|
||||||
addEndpointToGroup("Python", "file-to-pdf");
|
addEndpointToGroup("Python", "file-to-pdf");
|
||||||
|
|
||||||
// openCV
|
// openCV
|
||||||
@@ -244,6 +244,7 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Java", REMOVE_BLANKS);
|
addEndpointToGroup("Java", REMOVE_BLANKS);
|
||||||
addEndpointToGroup("Java", "pdf-to-text");
|
addEndpointToGroup("Java", "pdf-to-text");
|
||||||
addEndpointToGroup("Java", "remove-image-pdf");
|
addEndpointToGroup("Java", "remove-image-pdf");
|
||||||
|
addEndpointToGroup("Java", "pdf-to-markdown");
|
||||||
|
|
||||||
// Javascript
|
// Javascript
|
||||||
addEndpointToGroup("Javascript", "pdf-organizer");
|
addEndpointToGroup("Javascript", "pdf-organizer");
|
||||||
@@ -259,9 +260,11 @@ public class EndpointConfiguration {
|
|||||||
// Weasyprint dependent endpoints
|
// Weasyprint dependent endpoints
|
||||||
addEndpointToGroup("Weasyprint", "html-to-pdf");
|
addEndpointToGroup("Weasyprint", "html-to-pdf");
|
||||||
addEndpointToGroup("Weasyprint", "url-to-pdf");
|
addEndpointToGroup("Weasyprint", "url-to-pdf");
|
||||||
|
addEndpointToGroup("Weasyprint", "markdown-to-pdf");
|
||||||
|
|
||||||
// Pdftohtml dependent endpoints
|
// Pdftohtml dependent endpoints
|
||||||
addEndpointToGroup("Pdftohtml", "pdf-to-html");
|
addEndpointToGroup("Pdftohtml", "pdf-to-html");
|
||||||
|
addEndpointToGroup("Pdftohtml", "pdf-to-markdown");
|
||||||
|
|
||||||
// disabled for now while we resolve issues
|
// disabled for now while we resolve issues
|
||||||
disableEndpoint("pdf-to-pdfa");
|
disableEndpoint("pdf-to-pdfa");
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class InstallationPathConfig {
|
|||||||
// Pipeline paths
|
// Pipeline paths
|
||||||
private static final String PIPELINE_WATCHED_FOLDERS_PATH;
|
private static final String PIPELINE_WATCHED_FOLDERS_PATH;
|
||||||
private static final String PIPELINE_FINISHED_FOLDERS_PATH;
|
private static final String PIPELINE_FINISHED_FOLDERS_PATH;
|
||||||
|
private static final String PIPELINE_DEFAULT_WEB_UI_CONFIGS;
|
||||||
|
|
||||||
// Custom file paths
|
// Custom file paths
|
||||||
private static final String STATIC_PATH;
|
private static final String STATIC_PATH;
|
||||||
@@ -45,6 +46,7 @@ public class InstallationPathConfig {
|
|||||||
// Initialize pipeline paths
|
// Initialize pipeline paths
|
||||||
PIPELINE_WATCHED_FOLDERS_PATH = PIPELINE_PATH + "watchedFolders" + File.separator;
|
PIPELINE_WATCHED_FOLDERS_PATH = PIPELINE_PATH + "watchedFolders" + File.separator;
|
||||||
PIPELINE_FINISHED_FOLDERS_PATH = PIPELINE_PATH + "finishedFolders" + File.separator;
|
PIPELINE_FINISHED_FOLDERS_PATH = PIPELINE_PATH + "finishedFolders" + File.separator;
|
||||||
|
PIPELINE_DEFAULT_WEB_UI_CONFIGS = PIPELINE_PATH + "defaultWebUIConfigs" + File.separator;
|
||||||
|
|
||||||
// Initialize custom file paths
|
// Initialize custom file paths
|
||||||
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
|
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
|
||||||
@@ -118,6 +120,10 @@ public class InstallationPathConfig {
|
|||||||
return PIPELINE_FINISHED_FOLDERS_PATH;
|
return PIPELINE_FINISHED_FOLDERS_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getPipelineDefaultWebUIConfigsDir() {
|
||||||
|
return PIPELINE_DEFAULT_WEB_UI_CONFIGS;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getStaticPath() {
|
public static String getStaticPath() {
|
||||||
return STATIC_PATH;
|
return STATIC_PATH;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,24 @@
|
|||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.*;
|
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.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.DependsOn;
|
import org.springframework.context.annotation.DependsOn;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.security.authentication.ProviderManager;
|
import org.springframework.security.authentication.ProviderManager;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
|
||||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
|
||||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
|
||||||
import org.springframework.security.saml2.core.Saml2X509Credential;
|
|
||||||
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
|
|
||||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
|
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.registration.RelyingPartyRegistrationRepository;
|
||||||
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
|
||||||
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
|
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
@@ -43,24 +28,16 @@ import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
|||||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
|
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
|
||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
|
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
|
||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
|
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
|
||||||
import stirling.software.SPDF.config.security.saml2.CertificateUtils;
|
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationFailureHandler;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationFailureHandler;
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationSuccessHandler;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationSuccessHandler;
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2ResponseAuthenticationConverter;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2ResponseAuthenticationConverter;
|
||||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
|
||||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
|
||||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
|
||||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||||
import stirling.software.SPDF.repository.PersistentLoginRepository;
|
import stirling.software.SPDF.repository.PersistentLoginRepository;
|
||||||
|
|
||||||
@@ -72,7 +49,7 @@ import stirling.software.SPDF.repository.PersistentLoginRepository;
|
|||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
private final CustomUserDetailsService userDetailsService;
|
private final CustomUserDetailsService userDetailsService;
|
||||||
@Lazy private final UserService userService;
|
private final UserService userService;
|
||||||
|
|
||||||
@Qualifier("loginEnabled")
|
@Qualifier("loginEnabled")
|
||||||
private final boolean loginEnabledValue;
|
private final boolean loginEnabledValue;
|
||||||
@@ -86,16 +63,10 @@ public class SecurityConfiguration {
|
|||||||
private final FirstLoginFilter firstLoginFilter;
|
private final FirstLoginFilter firstLoginFilter;
|
||||||
private final SessionPersistentRegistry sessionRegistry;
|
private final SessionPersistentRegistry sessionRegistry;
|
||||||
private final PersistentLoginRepository persistentLoginRepository;
|
private final PersistentLoginRepository persistentLoginRepository;
|
||||||
|
private final GrantedAuthoritiesMapper oAuth2userAuthoritiesMapper;
|
||||||
|
private final RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations;
|
||||||
|
private final OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver;
|
||||||
|
|
||||||
// // Only Dev test
|
|
||||||
// @Bean
|
|
||||||
// public WebSecurityCustomizer webSecurityCustomizer() {
|
|
||||||
// return (web) ->
|
|
||||||
// web.ignoring()
|
|
||||||
// .requestMatchers(
|
|
||||||
// "/css/**", "/images/**", "/js/**", "/**.svg",
|
|
||||||
// "/pdfjs-legacy/**");
|
|
||||||
// }
|
|
||||||
public SecurityConfiguration(
|
public SecurityConfiguration(
|
||||||
PersistentLoginRepository persistentLoginRepository,
|
PersistentLoginRepository persistentLoginRepository,
|
||||||
CustomUserDetailsService userDetailsService,
|
CustomUserDetailsService userDetailsService,
|
||||||
@@ -106,7 +77,12 @@ public class SecurityConfiguration {
|
|||||||
UserAuthenticationFilter userAuthenticationFilter,
|
UserAuthenticationFilter userAuthenticationFilter,
|
||||||
LoginAttemptService loginAttemptService,
|
LoginAttemptService loginAttemptService,
|
||||||
FirstLoginFilter firstLoginFilter,
|
FirstLoginFilter firstLoginFilter,
|
||||||
SessionPersistentRegistry sessionRegistry) {
|
SessionPersistentRegistry sessionRegistry,
|
||||||
|
@Autowired(required = false) GrantedAuthoritiesMapper oAuth2userAuthoritiesMapper,
|
||||||
|
@Autowired(required = false)
|
||||||
|
RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations,
|
||||||
|
@Autowired(required = false)
|
||||||
|
OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver) {
|
||||||
this.userDetailsService = userDetailsService;
|
this.userDetailsService = userDetailsService;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.loginEnabledValue = loginEnabledValue;
|
this.loginEnabledValue = loginEnabledValue;
|
||||||
@@ -117,6 +93,9 @@ public class SecurityConfiguration {
|
|||||||
this.firstLoginFilter = firstLoginFilter;
|
this.firstLoginFilter = firstLoginFilter;
|
||||||
this.sessionRegistry = sessionRegistry;
|
this.sessionRegistry = sessionRegistry;
|
||||||
this.persistentLoginRepository = persistentLoginRepository;
|
this.persistentLoginRepository = persistentLoginRepository;
|
||||||
|
this.oAuth2userAuthoritiesMapper = oAuth2userAuthoritiesMapper;
|
||||||
|
this.saml2RelyingPartyRegistrations = saml2RelyingPartyRegistrations;
|
||||||
|
this.saml2AuthenticationRequestResolver = saml2AuthenticationRequestResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -274,7 +253,7 @@ public class SecurityConfiguration {
|
|||||||
userService,
|
userService,
|
||||||
loginAttemptService))
|
loginAttemptService))
|
||||||
.userAuthoritiesMapper(
|
.userAuthoritiesMapper(
|
||||||
userAuthoritiesMapper()))
|
oAuth2userAuthoritiesMapper))
|
||||||
.permitAll());
|
.permitAll());
|
||||||
}
|
}
|
||||||
// Handle SAML
|
// Handle SAML
|
||||||
@@ -291,7 +270,7 @@ public class SecurityConfiguration {
|
|||||||
try {
|
try {
|
||||||
saml2.loginPage("/saml2")
|
saml2.loginPage("/saml2")
|
||||||
.relyingPartyRegistrationRepository(
|
.relyingPartyRegistrationRepository(
|
||||||
relyingPartyRegistrations())
|
saml2RelyingPartyRegistrations)
|
||||||
.authenticationManager(
|
.authenticationManager(
|
||||||
new ProviderManager(authenticationProvider))
|
new ProviderManager(authenticationProvider))
|
||||||
.successHandler(
|
.successHandler(
|
||||||
@@ -302,8 +281,7 @@ public class SecurityConfiguration {
|
|||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomSaml2AuthenticationFailureHandler())
|
new CustomSaml2AuthenticationFailureHandler())
|
||||||
.authenticationRequestResolver(
|
.authenticationRequestResolver(
|
||||||
authenticationRequestResolver(
|
saml2AuthenticationRequestResolver);
|
||||||
relyingPartyRegistrations()));
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error configuring SAML2 login", e);
|
log.error("Error configuring SAML2 login", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@@ -311,244 +289,11 @@ public class SecurityConfiguration {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if (!applicationProperties.getSecurity().getCsrfDisabled()) {
|
|
||||||
// CookieCsrfTokenRepository cookieRepo =
|
|
||||||
// CookieCsrfTokenRepository.withHttpOnlyFalse();
|
|
||||||
// CsrfTokenRequestAttributeHandler requestHandler =
|
|
||||||
// new CsrfTokenRequestAttributeHandler();
|
|
||||||
// requestHandler.setCsrfRequestAttributeName(null);
|
|
||||||
// http.csrf(
|
|
||||||
// csrf ->
|
|
||||||
// csrf.csrfTokenRepository(cookieRepo)
|
|
||||||
// .csrfTokenRequestHandler(requestHandler));
|
|
||||||
// }
|
|
||||||
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
||||||
}
|
}
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnProperty(
|
|
||||||
value = "security.oauth2.enabled",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
|
||||||
List<ClientRegistration> registrations = new ArrayList<>();
|
|
||||||
githubClientRegistration().ifPresent(registrations::add);
|
|
||||||
oidcClientRegistration().ifPresent(registrations::add);
|
|
||||||
googleClientRegistration().ifPresent(registrations::add);
|
|
||||||
keycloakClientRegistration().ifPresent(registrations::add);
|
|
||||||
if (registrations.isEmpty()) {
|
|
||||||
log.error("At least one OAuth2 provider must be configured");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
return new InMemoryClientRegistrationRepository(registrations);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ClientRegistration> googleClientRegistration() {
|
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
|
||||||
if (oauth == null || !oauth.getEnabled()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
Client client = oauth.getClient();
|
|
||||||
if (client == null) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
GoogleProvider google = client.getGoogle();
|
|
||||||
return google != null && google.isSettingsValid()
|
|
||||||
? Optional.of(
|
|
||||||
ClientRegistration.withRegistrationId(google.getName())
|
|
||||||
.clientId(google.getClientId())
|
|
||||||
.clientSecret(google.getClientSecret())
|
|
||||||
.scope(google.getScopes())
|
|
||||||
.authorizationUri(google.getAuthorizationuri())
|
|
||||||
.tokenUri(google.getTokenuri())
|
|
||||||
.userInfoUri(google.getUserinfouri())
|
|
||||||
.userNameAttributeName(google.getUseAsUsername())
|
|
||||||
.clientName(google.getClientName())
|
|
||||||
.redirectUri("{baseUrl}/login/oauth2/code/" + google.getName())
|
|
||||||
.authorizationGrantType(
|
|
||||||
org.springframework.security.oauth2.core
|
|
||||||
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
|
||||||
.build())
|
|
||||||
: Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ClientRegistration> keycloakClientRegistration() {
|
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
|
||||||
if (oauth == null || !oauth.getEnabled()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
Client client = oauth.getClient();
|
|
||||||
if (client == null) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
KeycloakProvider keycloak = client.getKeycloak();
|
|
||||||
return keycloak != null && keycloak.isSettingsValid()
|
|
||||||
? Optional.of(
|
|
||||||
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
|
||||||
.registrationId(keycloak.getName())
|
|
||||||
.clientId(keycloak.getClientId())
|
|
||||||
.clientSecret(keycloak.getClientSecret())
|
|
||||||
.scope(keycloak.getScopes())
|
|
||||||
.userNameAttributeName(keycloak.getUseAsUsername())
|
|
||||||
.clientName(keycloak.getClientName())
|
|
||||||
.build())
|
|
||||||
: Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ClientRegistration> githubClientRegistration() {
|
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
|
||||||
if (oauth == null || !oauth.getEnabled()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
Client client = oauth.getClient();
|
|
||||||
if (client == null) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
GithubProvider github = client.getGithub();
|
|
||||||
return github != null && github.isSettingsValid()
|
|
||||||
? Optional.of(
|
|
||||||
ClientRegistration.withRegistrationId(github.getName())
|
|
||||||
.clientId(github.getClientId())
|
|
||||||
.clientSecret(github.getClientSecret())
|
|
||||||
.scope(github.getScopes())
|
|
||||||
.authorizationUri(github.getAuthorizationuri())
|
|
||||||
.tokenUri(github.getTokenuri())
|
|
||||||
.userInfoUri(github.getUserinfouri())
|
|
||||||
.userNameAttributeName(github.getUseAsUsername())
|
|
||||||
.clientName(github.getClientName())
|
|
||||||
.redirectUri("{baseUrl}/login/oauth2/code/" + github.getName())
|
|
||||||
.authorizationGrantType(
|
|
||||||
org.springframework.security.oauth2.core
|
|
||||||
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
|
||||||
.build())
|
|
||||||
: Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ClientRegistration> oidcClientRegistration() {
|
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
|
||||||
if (oauth == null
|
|
||||||
|| oauth.getIssuer() == null
|
|
||||||
|| oauth.getIssuer().isEmpty()
|
|
||||||
|| oauth.getClientId() == null
|
|
||||||
|| oauth.getClientId().isEmpty()
|
|
||||||
|| oauth.getClientSecret() == null
|
|
||||||
|| oauth.getClientSecret().isEmpty()
|
|
||||||
|| oauth.getScopes() == null
|
|
||||||
|| oauth.getScopes().isEmpty()
|
|
||||||
|| oauth.getUseAsUsername() == null
|
|
||||||
|| oauth.getUseAsUsername().isEmpty()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
return Optional.of(
|
|
||||||
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
|
||||||
.registrationId("oidc")
|
|
||||||
.clientId(oauth.getClientId())
|
|
||||||
.clientSecret(oauth.getClientSecret())
|
|
||||||
.scope(oauth.getScopes())
|
|
||||||
.userNameAttributeName(oauth.getUseAsUsername())
|
|
||||||
.clientName("OIDC")
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnProperty(
|
|
||||||
name = "security.saml2.enabled",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
|
||||||
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
|
||||||
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
|
||||||
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
|
|
||||||
Resource privateKeyResource = samlConf.getPrivateKey();
|
|
||||||
Resource certificateResource = samlConf.getSpCert();
|
|
||||||
Saml2X509Credential signingCredential =
|
|
||||||
new Saml2X509Credential(
|
|
||||||
CertificateUtils.readPrivateKey(privateKeyResource),
|
|
||||||
CertificateUtils.readCertificate(certificateResource),
|
|
||||||
Saml2X509CredentialType.SIGNING);
|
|
||||||
RelyingPartyRegistration rp =
|
|
||||||
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
|
|
||||||
.signingX509Credentials(c -> c.add(signingCredential))
|
|
||||||
.assertingPartyMetadata(
|
|
||||||
metadata ->
|
|
||||||
metadata.entityId(samlConf.getIdpIssuer())
|
|
||||||
.singleSignOnServiceLocation(
|
|
||||||
samlConf.getIdpSingleLoginUrl())
|
|
||||||
.verificationX509Credentials(
|
|
||||||
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() {
|
public DaoAuthenticationProvider daoAuthenticationProvider() {
|
||||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||||
provider.setUserDetailsService(userDetailsService);
|
provider.setUserDetailsService(userDetailsService);
|
||||||
@@ -556,46 +301,6 @@ public class SecurityConfiguration {
|
|||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
|
|
||||||
This is required for the internal; 'hasRole()' function to give out the correct role.
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnProperty(
|
|
||||||
value = "security.oauth2.enabled",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
|
||||||
return (authorities) -> {
|
|
||||||
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
|
||||||
authorities.forEach(
|
|
||||||
authority -> {
|
|
||||||
// Add existing OAUTH2 Authorities
|
|
||||||
mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
|
|
||||||
// Add Authorities from database for existing user, if user is present.
|
|
||||||
if (authority instanceof OAuth2UserAuthority oauth2Auth) {
|
|
||||||
String useAsUsername =
|
|
||||||
applicationProperties
|
|
||||||
.getSecurity()
|
|
||||||
.getOauth2()
|
|
||||||
.getUseAsUsername();
|
|
||||||
Optional<User> userOpt =
|
|
||||||
userService.findByUsernameIgnoreCase(
|
|
||||||
(String) oauth2Auth.getAttributes().get(useAsUsername));
|
|
||||||
if (userOpt.isPresent()) {
|
|
||||||
User user = userOpt.get();
|
|
||||||
if (user != null) {
|
|
||||||
mappedAuthorities.add(
|
|
||||||
new SimpleGrantedAuthority(
|
|
||||||
userService.findRole(user).getAuthority()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return mappedAuthorities;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public IPRateLimitingFilter rateLimitingFilter() {
|
public IPRateLimitingFilter rateLimitingFilter() {
|
||||||
// Example limit TODO add config level
|
// Example limit TODO add config level
|
||||||
|
|||||||
@@ -329,12 +329,16 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
public boolean isUsernameValid(String username) {
|
public boolean isUsernameValid(String username) {
|
||||||
// Checks whether the simple username is formatted correctly
|
// Checks whether the simple username is formatted correctly
|
||||||
|
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
||||||
boolean isValidSimpleUsername =
|
boolean isValidSimpleUsername =
|
||||||
username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$");
|
username.matches("^[a-zA-Z0-9](?!.*[-@._+]{2,})[a-zA-Z0-9@._+-]{1,48}[a-zA-Z0-9]$");
|
||||||
|
|
||||||
// Checks whether the email address is formatted correctly
|
// Checks whether the email address is formatted correctly
|
||||||
|
// Regular expression for email addresses: Max. 320 characters, with RFC-like validation
|
||||||
boolean isValidEmail =
|
boolean isValidEmail =
|
||||||
username.matches(
|
username.matches(
|
||||||
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
|
"^(?=.{1,320}$)(?=.{1,64}@)[A-Za-z0-9](?:[A-Za-z0-9_.+-]*[A-Za-z0-9])?@[^-][A-Za-z0-9-]+(?:\\\\.[A-Za-z0-9-]+)*(?:\\\\.[A-Za-z]{2,})$");
|
||||||
|
|
||||||
List<String> notAllowedUserList = new ArrayList<>();
|
List<String> notAllowedUserList = new ArrayList<>();
|
||||||
notAllowedUserList.add("ALL_USERS".toLowerCase());
|
notAllowedUserList.add("ALL_USERS".toLowerCase());
|
||||||
boolean notAllowedUser = notAllowedUserList.contains(username.toLowerCase());
|
boolean notAllowedUser = notAllowedUserList.contains(username.toLowerCase());
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.config.security.database;
|
package stirling.software.SPDF.config.security.database;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
@@ -9,6 +11,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||||
|
|
||||||
@@ -17,8 +20,8 @@ import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class DatabaseConfig {
|
public class DatabaseConfig {
|
||||||
|
|
||||||
public static final String DATASOURCE_DEFAULT_URL =
|
public final String DATASOURCE_DEFAULT_URL;
|
||||||
"jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
|
||||||
public static final String DATASOURCE_URL_TEMPLATE = "jdbc:%s://%s:%4d/%s";
|
public static final String DATASOURCE_URL_TEMPLATE = "jdbc:%s://%s:%4d/%s";
|
||||||
public static final String DEFAULT_DRIVER = "org.h2.Driver";
|
public static final String DEFAULT_DRIVER = "org.h2.Driver";
|
||||||
public static final String DEFAULT_USERNAME = "sa";
|
public static final String DEFAULT_USERNAME = "sa";
|
||||||
@@ -27,7 +30,14 @@ public class DatabaseConfig {
|
|||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private final boolean runningEE;
|
private final boolean runningEE;
|
||||||
|
|
||||||
public DatabaseConfig(ApplicationProperties applicationProperties, @Qualifier("runningEE") boolean runningEE) {
|
public DatabaseConfig(
|
||||||
|
ApplicationProperties applicationProperties,
|
||||||
|
@Qualifier("runningEE") boolean runningEE) {
|
||||||
|
DATASOURCE_DEFAULT_URL =
|
||||||
|
"jdbc:h2:file:"
|
||||||
|
+ InstallationPathConfig.getConfigPath()
|
||||||
|
+ File.separator
|
||||||
|
+ "stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.runningEE = runningEE;
|
this.runningEE = runningEE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.springframework.jdbc.datasource.init.ScriptException;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.exception.BackupNotFoundException;
|
import stirling.software.SPDF.model.exception.BackupNotFoundException;
|
||||||
@@ -37,12 +38,14 @@ public class DatabaseService implements DatabaseInterface {
|
|||||||
|
|
||||||
public static final String BACKUP_PREFIX = "backup_";
|
public static final String BACKUP_PREFIX = "backup_";
|
||||||
public static final String SQL_SUFFIX = ".sql";
|
public static final String SQL_SUFFIX = ".sql";
|
||||||
private static final String BACKUP_DIR = "configs/db/backup/";
|
private final Path BACKUP_DIR;
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
|
|
||||||
public DatabaseService(ApplicationProperties applicationProperties, DataSource dataSource) {
|
public DatabaseService(ApplicationProperties applicationProperties, DataSource dataSource) {
|
||||||
|
this.BACKUP_DIR =
|
||||||
|
Paths.get(InstallationPathConfig.getConfigPath(), "db", "backup").normalize();
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
}
|
}
|
||||||
@@ -55,9 +58,9 @@ public class DatabaseService implements DatabaseInterface {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean hasBackup() {
|
public boolean hasBackup() {
|
||||||
Path filePath = Paths.get(BACKUP_DIR);
|
createBackupDirectory();
|
||||||
|
|
||||||
if (Files.exists(filePath)) {
|
if (Files.exists(BACKUP_DIR)) {
|
||||||
return !getBackupList().isEmpty();
|
return !getBackupList().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,11 +77,11 @@ public class DatabaseService implements DatabaseInterface {
|
|||||||
List<FileInfo> backupFiles = new ArrayList<>();
|
List<FileInfo> backupFiles = new ArrayList<>();
|
||||||
|
|
||||||
if (isH2Database()) {
|
if (isH2Database()) {
|
||||||
Path backupPath = Paths.get(BACKUP_DIR);
|
createBackupDirectory();
|
||||||
|
|
||||||
try (DirectoryStream<Path> stream =
|
try (DirectoryStream<Path> stream =
|
||||||
Files.newDirectoryStream(
|
Files.newDirectoryStream(
|
||||||
backupPath,
|
BACKUP_DIR,
|
||||||
path ->
|
path ->
|
||||||
path.getFileName().toString().startsWith(BACKUP_PREFIX)
|
path.getFileName().toString().startsWith(BACKUP_PREFIX)
|
||||||
&& path.getFileName()
|
&& path.getFileName()
|
||||||
@@ -110,6 +113,17 @@ public class DatabaseService implements DatabaseInterface {
|
|||||||
return backupFiles;
|
return backupFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createBackupDirectory() {
|
||||||
|
if (!Files.exists(BACKUP_DIR)) {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(BACKUP_DIR);
|
||||||
|
log.debug("create backup directory: {}", BACKUP_DIR);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error create backup directory: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void importDatabase() {
|
public void importDatabase() {
|
||||||
if (!hasBackup()) throw new BackupNotFoundException("No backup scripts were found.");
|
if (!hasBackup()) throw new BackupNotFoundException("No backup scripts were found.");
|
||||||
@@ -255,7 +269,8 @@ public class DatabaseService implements DatabaseInterface {
|
|||||||
* @return the <code>Path</code> object for the given file name
|
* @return the <code>Path</code> object for the given file name
|
||||||
*/
|
*/
|
||||||
public Path getBackupFilePath(String fileName) {
|
public Path getBackupFilePath(String fileName) {
|
||||||
Path filePath = Paths.get(BACKUP_DIR, fileName).normalize();
|
createBackupDirectory();
|
||||||
|
Path filePath = BACKUP_DIR.resolve(fileName).normalize();
|
||||||
if (!filePath.startsWith(BACKUP_DIR)) {
|
if (!filePath.startsWith(BACKUP_DIR)) {
|
||||||
throw new SecurityException("Path traversal detected");
|
throw new SecurityException("Path traversal detected");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
package stirling.software.SPDF.config.security.oauth2;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||||
|
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
import stirling.software.SPDF.model.provider.GithubProvider;
|
||||||
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||||
|
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
value = "security.oauth2.enabled",
|
||||||
|
havingValue = "true",
|
||||||
|
matchIfMissing = false)
|
||||||
|
public class OAuth2Configuration {
|
||||||
|
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
@Lazy private final UserService userService;
|
||||||
|
|
||||||
|
public OAuth2Configuration(
|
||||||
|
ApplicationProperties applicationProperties, @Lazy UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
value = "security.oauth2.enabled",
|
||||||
|
havingValue = "true",
|
||||||
|
matchIfMissing = false)
|
||||||
|
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||||
|
List<ClientRegistration> registrations = new ArrayList<>();
|
||||||
|
githubClientRegistration().ifPresent(registrations::add);
|
||||||
|
oidcClientRegistration().ifPresent(registrations::add);
|
||||||
|
googleClientRegistration().ifPresent(registrations::add);
|
||||||
|
keycloakClientRegistration().ifPresent(registrations::add);
|
||||||
|
if (registrations.isEmpty()) {
|
||||||
|
log.error("At least one OAuth2 provider must be configured");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
return new InMemoryClientRegistrationRepository(registrations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> googleClientRegistration() {
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||||
|
if (oauth == null || !oauth.getEnabled()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
Client client = oauth.getClient();
|
||||||
|
if (client == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
GoogleProvider google = client.getGoogle();
|
||||||
|
return google != null && google.isSettingsValid()
|
||||||
|
? Optional.of(
|
||||||
|
ClientRegistration.withRegistrationId(google.getName())
|
||||||
|
.clientId(google.getClientId())
|
||||||
|
.clientSecret(google.getClientSecret())
|
||||||
|
.scope(google.getScopes())
|
||||||
|
.authorizationUri(google.getAuthorizationuri())
|
||||||
|
.tokenUri(google.getTokenuri())
|
||||||
|
.userInfoUri(google.getUserinfouri())
|
||||||
|
.userNameAttributeName(google.getUseAsUsername())
|
||||||
|
.clientName(google.getClientName())
|
||||||
|
.redirectUri("{baseUrl}/login/oauth2/code/" + google.getName())
|
||||||
|
.authorizationGrantType(
|
||||||
|
org.springframework.security.oauth2.core
|
||||||
|
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.build())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> keycloakClientRegistration() {
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||||
|
if (oauth == null || !oauth.getEnabled()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
Client client = oauth.getClient();
|
||||||
|
if (client == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
KeycloakProvider keycloak = client.getKeycloak();
|
||||||
|
return keycloak != null && keycloak.isSettingsValid()
|
||||||
|
? Optional.of(
|
||||||
|
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
||||||
|
.registrationId(keycloak.getName())
|
||||||
|
.clientId(keycloak.getClientId())
|
||||||
|
.clientSecret(keycloak.getClientSecret())
|
||||||
|
.scope(keycloak.getScopes())
|
||||||
|
.userNameAttributeName(keycloak.getUseAsUsername())
|
||||||
|
.clientName(keycloak.getClientName())
|
||||||
|
.build())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> githubClientRegistration() {
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||||
|
if (oauth == null || !oauth.getEnabled()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
Client client = oauth.getClient();
|
||||||
|
if (client == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
GithubProvider github = client.getGithub();
|
||||||
|
return github != null && github.isSettingsValid()
|
||||||
|
? Optional.of(
|
||||||
|
ClientRegistration.withRegistrationId(github.getName())
|
||||||
|
.clientId(github.getClientId())
|
||||||
|
.clientSecret(github.getClientSecret())
|
||||||
|
.scope(github.getScopes())
|
||||||
|
.authorizationUri(github.getAuthorizationuri())
|
||||||
|
.tokenUri(github.getTokenuri())
|
||||||
|
.userInfoUri(github.getUserinfouri())
|
||||||
|
.userNameAttributeName(github.getUseAsUsername())
|
||||||
|
.clientName(github.getClientName())
|
||||||
|
.redirectUri("{baseUrl}/login/oauth2/code/" + github.getName())
|
||||||
|
.authorizationGrantType(
|
||||||
|
org.springframework.security.oauth2.core
|
||||||
|
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.build())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> oidcClientRegistration() {
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||||
|
if (oauth == null
|
||||||
|
|| oauth.getIssuer() == null
|
||||||
|
|| oauth.getIssuer().isEmpty()
|
||||||
|
|| oauth.getClientId() == null
|
||||||
|
|| oauth.getClientId().isEmpty()
|
||||||
|
|| oauth.getClientSecret() == null
|
||||||
|
|| oauth.getClientSecret().isEmpty()
|
||||||
|
|| oauth.getScopes() == null
|
||||||
|
|| oauth.getScopes().isEmpty()
|
||||||
|
|| oauth.getUseAsUsername() == null
|
||||||
|
|| oauth.getUseAsUsername().isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(
|
||||||
|
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
||||||
|
.registrationId("oidc")
|
||||||
|
.clientId(oauth.getClientId())
|
||||||
|
.clientSecret(oauth.getClientSecret())
|
||||||
|
.scope(oauth.getScopes())
|
||||||
|
.userNameAttributeName(oauth.getUseAsUsername())
|
||||||
|
.clientName("OIDC")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
|
||||||
|
This is required for the internal; 'hasRole()' function to give out the correct role.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
value = "security.oauth2.enabled",
|
||||||
|
havingValue = "true",
|
||||||
|
matchIfMissing = false)
|
||||||
|
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
||||||
|
return (authorities) -> {
|
||||||
|
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
||||||
|
authorities.forEach(
|
||||||
|
authority -> {
|
||||||
|
// Add existing OAUTH2 Authorities
|
||||||
|
mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
|
||||||
|
// Add Authorities from database for existing user, if user is present.
|
||||||
|
if (authority instanceof OAuth2UserAuthority oauth2Auth) {
|
||||||
|
String useAsUsername =
|
||||||
|
applicationProperties
|
||||||
|
.getSecurity()
|
||||||
|
.getOauth2()
|
||||||
|
.getUseAsUsername();
|
||||||
|
Optional<User> userOpt =
|
||||||
|
userService.findByUsernameIgnoreCase(
|
||||||
|
(String) oauth2Auth.getAttributes().get(useAsUsername));
|
||||||
|
if (userOpt.isPresent()) {
|
||||||
|
User user = userOpt.get();
|
||||||
|
if (user != null) {
|
||||||
|
mappedAuthorities.add(
|
||||||
|
new SimpleGrantedAuthority(
|
||||||
|
userService.findRole(user).getAuthority()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mappedAuthorities;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package stirling.software.SPDF.config.security.saml2;
|
||||||
|
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.security.saml2.core.Saml2X509Credential;
|
||||||
|
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
|
||||||
|
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.registration.Saml2MessageBinding;
|
||||||
|
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
value = "security.saml2.enabled",
|
||||||
|
havingValue = "true",
|
||||||
|
matchIfMissing = false)
|
||||||
|
public class SAML2Configuration {
|
||||||
|
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
public SAML2Configuration(ApplicationProperties applicationProperties) {
|
||||||
|
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
name = "security.saml2.enabled",
|
||||||
|
havingValue = "true",
|
||||||
|
matchIfMissing = false)
|
||||||
|
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
||||||
|
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
||||||
|
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
||||||
|
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
|
||||||
|
Resource privateKeyResource = samlConf.getPrivateKey();
|
||||||
|
Resource certificateResource = samlConf.getSpCert();
|
||||||
|
Saml2X509Credential signingCredential =
|
||||||
|
new Saml2X509Credential(
|
||||||
|
CertificateUtils.readPrivateKey(privateKeyResource),
|
||||||
|
CertificateUtils.readCertificate(certificateResource),
|
||||||
|
Saml2X509CredentialType.SIGNING);
|
||||||
|
RelyingPartyRegistration rp =
|
||||||
|
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
|
||||||
|
.signingX509Credentials(c -> c.add(signingCredential))
|
||||||
|
.assertingPartyMetadata(
|
||||||
|
metadata ->
|
||||||
|
metadata.entityId(samlConf.getIdpIssuer())
|
||||||
|
.singleSignOnServiceLocation(
|
||||||
|
samlConf.getIdpSingleLoginUrl())
|
||||||
|
.verificationX509Credentials(
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
|
import org.apache.pdfbox.cos.COSName;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
|
||||||
|
import org.apache.pdfbox.pdmodel.encryption.PDEncryption;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import stirling.software.SPDF.model.api.PDFFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/analysis")
|
||||||
|
@Tag(name = "Analysis", description = "Analysis APIs")
|
||||||
|
public class AnalysisController {
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping(value = "/page-count", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get PDF page count",
|
||||||
|
description = "Returns total number of pages in PDF. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public Map<String, Integer> getPageCount(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
return Map.of("pageCount", document.getNumberOfPages());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value ="/basic-info", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get basic PDF information",
|
||||||
|
description = "Returns page count, version, file size. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public Map<String, Object> getBasicInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
Map<String, Object> info = new HashMap<>();
|
||||||
|
info.put("pageCount", document.getNumberOfPages());
|
||||||
|
info.put("pdfVersion", document.getVersion());
|
||||||
|
info.put("fileSize", file.getFileInput().getSize());
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value ="/document-properties", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get PDF document properties",
|
||||||
|
description = "Returns title, author, subject, etc. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public Map<String, String> getDocumentProperties(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
PDDocumentInformation info = document.getDocumentInformation();
|
||||||
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
properties.put("title", info.getTitle());
|
||||||
|
properties.put("author", info.getAuthor());
|
||||||
|
properties.put("subject", info.getSubject());
|
||||||
|
properties.put("keywords", info.getKeywords());
|
||||||
|
properties.put("creator", info.getCreator());
|
||||||
|
properties.put("producer", info.getProducer());
|
||||||
|
properties.put("creationDate", info.getCreationDate().toString());
|
||||||
|
properties.put("modificationDate", info.getModificationDate().toString());
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value ="/page-dimensions", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get page dimensions for all pages",
|
||||||
|
description = "Returns width and height of each page. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public List<Map<String, Float>> getPageDimensions(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
List<Map<String, Float>> dimensions = new ArrayList<>();
|
||||||
|
PDPageTree pages = document.getPages();
|
||||||
|
|
||||||
|
for (PDPage page : pages) {
|
||||||
|
Map<String, Float> pageDim = new HashMap<>();
|
||||||
|
pageDim.put("width", page.getBBox().getWidth());
|
||||||
|
pageDim.put("height", page.getBBox().getHeight());
|
||||||
|
dimensions.add(pageDim);
|
||||||
|
}
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value ="/form-fields", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get form field information",
|
||||||
|
description = "Returns count and details of form fields. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public Map<String, Object> getFormFields(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
Map<String, Object> formInfo = new HashMap<>();
|
||||||
|
PDAcroForm form = document.getDocumentCatalog().getAcroForm();
|
||||||
|
|
||||||
|
if (form != null) {
|
||||||
|
formInfo.put("fieldCount", form.getFields().size());
|
||||||
|
formInfo.put("hasXFA", form.hasXFA());
|
||||||
|
formInfo.put("isSignaturesExist", form.isSignaturesExist());
|
||||||
|
} else {
|
||||||
|
formInfo.put("fieldCount", 0);
|
||||||
|
formInfo.put("hasXFA", false);
|
||||||
|
formInfo.put("isSignaturesExist", false);
|
||||||
|
}
|
||||||
|
return formInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value ="/annotation-info", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get annotation information",
|
||||||
|
description = "Returns count and types of annotations. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public Map<String, Object> getAnnotationInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
Map<String, Object> annotInfo = new HashMap<>();
|
||||||
|
int totalAnnotations = 0;
|
||||||
|
Map<String, Integer> annotationTypes = new HashMap<>();
|
||||||
|
|
||||||
|
for (PDPage page : document.getPages()) {
|
||||||
|
for (PDAnnotation annot : page.getAnnotations()) {
|
||||||
|
totalAnnotations++;
|
||||||
|
String subType = annot.getSubtype();
|
||||||
|
annotationTypes.merge(subType, 1, Integer::sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
annotInfo.put("totalCount", totalAnnotations);
|
||||||
|
annotInfo.put("typeBreakdown", annotationTypes);
|
||||||
|
return annotInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value ="/font-info", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get font information",
|
||||||
|
description = "Returns list of fonts used in the document. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public Map<String, Object> getFontInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
Map<String, Object> fontInfo = new HashMap<>();
|
||||||
|
Set<String> fontNames = new HashSet<>();
|
||||||
|
|
||||||
|
for (PDPage page : document.getPages()) {
|
||||||
|
for (COSName font : page.getResources().getFontNames()) {
|
||||||
|
fontNames.add(font.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fontInfo.put("fontCount", fontNames.size());
|
||||||
|
fontInfo.put("fonts", fontNames);
|
||||||
|
return fontInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value ="/security-info", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Get security information",
|
||||||
|
description = "Returns encryption and permission details. Input:PDF Output:JSON Type:SISO")
|
||||||
|
public Map<String, Object> getSecurityInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
|
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
|
||||||
|
Map<String, Object> securityInfo = new HashMap<>();
|
||||||
|
PDEncryption encryption = document.getEncryption();
|
||||||
|
|
||||||
|
if (encryption != null) {
|
||||||
|
securityInfo.put("isEncrypted", true);
|
||||||
|
securityInfo.put("keyLength", encryption.getLength());
|
||||||
|
|
||||||
|
// Get permissions
|
||||||
|
Map<String, Boolean> permissions = new HashMap<>();
|
||||||
|
permissions.put("canPrint", document.getCurrentAccessPermission().canPrint());
|
||||||
|
permissions.put("canModify", document.getCurrentAccessPermission().canModify());
|
||||||
|
permissions.put("canExtractContent", document.getCurrentAccessPermission().canExtractContent());
|
||||||
|
permissions.put("canModifyAnnotations", document.getCurrentAccessPermission().canModifyAnnotations());
|
||||||
|
|
||||||
|
securityInfo.put("permissions", permissions);
|
||||||
|
} else {
|
||||||
|
securityInfo.put("isEncrypted", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return securityInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.PDFText;
|
import stirling.software.SPDF.model.PDFText;
|
||||||
import stirling.software.SPDF.model.api.security.ManualRedactPdfRequest;
|
import stirling.software.SPDF.model.api.security.ManualRedactPdfRequest;
|
||||||
import stirling.software.SPDF.model.api.security.RedactPdfRequest;
|
import stirling.software.SPDF.model.api.security.RedactPdfRequest;
|
||||||
@@ -53,12 +52,17 @@ public class RedactController {
|
|||||||
|
|
||||||
@InitBinder
|
@InitBinder
|
||||||
public void initBinder(WebDataBinder binder) {
|
public void initBinder(WebDataBinder binder) {
|
||||||
binder.registerCustomEditor(List.class, "redactions", new StringToArrayListPropertyEditor());
|
binder.registerCustomEditor(
|
||||||
|
List.class, "redactions", new StringToArrayListPropertyEditor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/redact", consumes = "multipart/form-data")
|
@PostMapping(value = "/redact", consumes = "multipart/form-data")
|
||||||
@Operation(summary = "Redacts areas and pages in a PDF document", description = "This operation takes an input PDF file with a list of areas, page number(s)/range(s)/function(s) to redact. Input:PDF, Output:PDF, Type:SISO")
|
@Operation(
|
||||||
public ResponseEntity<byte[]> redactPDF(@ModelAttribute ManualRedactPdfRequest request) throws IOException {
|
summary = "Redacts areas and pages in a PDF document",
|
||||||
|
description =
|
||||||
|
"This operation takes an input PDF file with a list of areas, page number(s)/range(s)/function(s) to redact. Input:PDF, Output:PDF, Type:SISO")
|
||||||
|
public ResponseEntity<byte[]> redactPDF(@ModelAttribute ManualRedactPdfRequest request)
|
||||||
|
throws IOException {
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
List<RedactionArea> redactionAreas = request.getRedactions();
|
List<RedactionArea> redactionAreas = request.getRedactions();
|
||||||
|
|
||||||
@@ -86,18 +90,22 @@ public class RedactController {
|
|||||||
+ "_redacted.pdf");
|
+ "_redacted.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void redactAreas(List<RedactionArea> redactionAreas, PDDocument document, PDPageTree allPages)
|
private void redactAreas(
|
||||||
|
List<RedactionArea> redactionAreas, PDDocument document, PDPageTree allPages)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Color redactColor = null;
|
Color redactColor = null;
|
||||||
for (RedactionArea redactionArea : redactionAreas) {
|
for (RedactionArea redactionArea : redactionAreas) {
|
||||||
if (redactionArea.getPage() == null || redactionArea.getPage() <= 0
|
if (redactionArea.getPage() == null
|
||||||
|| redactionArea.getHeight() == null || redactionArea.getHeight() <= 0.0D
|
|| redactionArea.getPage() <= 0
|
||||||
|| redactionArea.getWidth() == null || redactionArea.getWidth() <= 0.0D)
|
|| redactionArea.getHeight() == null
|
||||||
continue;
|
|| redactionArea.getHeight() <= 0.0D
|
||||||
|
|| redactionArea.getWidth() == null
|
||||||
|
|| redactionArea.getWidth() <= 0.0D) continue;
|
||||||
PDPage page = allPages.get(redactionArea.getPage() - 1);
|
PDPage page = allPages.get(redactionArea.getPage() - 1);
|
||||||
|
|
||||||
PDPageContentStream contentStream = new PDPageContentStream(
|
PDPageContentStream contentStream =
|
||||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
new PDPageContentStream(
|
||||||
|
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||||
redactColor = decodeOrDefault(redactionArea.getColor(), Color.BLACK);
|
redactColor = decodeOrDefault(redactionArea.getColor(), Color.BLACK);
|
||||||
contentStream.setNonStrokingColor(redactColor);
|
contentStream.setNonStrokingColor(redactColor);
|
||||||
|
|
||||||
@@ -114,15 +122,17 @@ public class RedactController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void redactPages(ManualRedactPdfRequest request, PDDocument document, PDPageTree allPages)
|
private void redactPages(
|
||||||
|
ManualRedactPdfRequest request, PDDocument document, PDPageTree allPages)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Color redactColor = decodeOrDefault(request.getPageRedactionColor(), Color.BLACK);
|
Color redactColor = decodeOrDefault(request.getPageRedactionColor(), Color.BLACK);
|
||||||
List<Integer> pageNumbers = getPageNumbers(request, allPages.getCount());
|
List<Integer> pageNumbers = getPageNumbers(request, allPages.getCount());
|
||||||
for (Integer pageNumber : pageNumbers) {
|
for (Integer pageNumber : pageNumbers) {
|
||||||
PDPage page = allPages.get(pageNumber);
|
PDPage page = allPages.get(pageNumber);
|
||||||
|
|
||||||
PDPageContentStream contentStream = new PDPageContentStream(
|
PDPageContentStream contentStream =
|
||||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
new PDPageContentStream(
|
||||||
|
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||||
contentStream.setNonStrokingColor(redactColor);
|
contentStream.setNonStrokingColor(redactColor);
|
||||||
|
|
||||||
PDRectangle box = page.getBBox();
|
PDRectangle box = page.getBBox();
|
||||||
@@ -146,8 +156,10 @@ public class RedactController {
|
|||||||
|
|
||||||
private List<Integer> getPageNumbers(ManualRedactPdfRequest request, int pagesCount) {
|
private List<Integer> getPageNumbers(ManualRedactPdfRequest request, int pagesCount) {
|
||||||
String pageNumbersInput = request.getPageNumbers();
|
String pageNumbersInput = request.getPageNumbers();
|
||||||
String[] parsedPageNumbers = pageNumbersInput != null ? pageNumbersInput.split(",") : new String[0];
|
String[] parsedPageNumbers =
|
||||||
List<Integer> pageNumbers = GeneralUtils.parsePageList(parsedPageNumbers, pagesCount, false);
|
pageNumbersInput != null ? pageNumbersInput.split(",") : new String[0];
|
||||||
|
List<Integer> pageNumbers =
|
||||||
|
GeneralUtils.parsePageList(parsedPageNumbers, pagesCount, false);
|
||||||
Collections.sort(pageNumbers);
|
Collections.sort(pageNumbers);
|
||||||
return pageNumbers;
|
return pageNumbers;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ public class WatermarkController {
|
|||||||
+ Math.abs(watermarkHeight * Math.cos(radians)));
|
+ Math.abs(watermarkHeight * Math.cos(radians)));
|
||||||
|
|
||||||
// Calculating the number of rows and columns.
|
// Calculating the number of rows and columns.
|
||||||
|
|
||||||
int watermarkRows = (int) (pageHeight / newWatermarkHeight + 1);
|
int watermarkRows = (int) (pageHeight / newWatermarkHeight + 1);
|
||||||
int watermarkCols = (int) (pageWidth / newWatermarkWidth + 1);
|
int watermarkCols = (int) (pageWidth / newWatermarkWidth + 1);
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ public class ConverterWebController {
|
|||||||
return "convert/markdown-to-pdf";
|
return "convert/markdown-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/pdf-to-markdown")
|
||||||
|
@Hidden
|
||||||
|
public String convertPdfToMarkdownForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "pdf-to-markdown");
|
||||||
|
return "convert/pdf-to-markdown";
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/url-to-pdf")
|
@GetMapping("/url-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertURLToPdfForm(Model model) {
|
public String convertURLToPdfForm(Model model) {
|
||||||
|
|||||||
@@ -54,8 +54,11 @@ public class GeneralWebController {
|
|||||||
model.addAttribute("currentPage", "pipeline");
|
model.addAttribute("currentPage", "pipeline");
|
||||||
List<String> pipelineConfigs = new ArrayList<>();
|
List<String> pipelineConfigs = new ArrayList<>();
|
||||||
List<Map<String, String>> pipelineConfigsWithNames = new ArrayList<>();
|
List<Map<String, String>> pipelineConfigsWithNames = new ArrayList<>();
|
||||||
if (new File("./pipeline/defaultWebUIConfigs/").exists()) {
|
if (new File(InstallationPathConfig.getPipelineDefaultWebUIConfigsDir()).exists()) {
|
||||||
try (Stream<Path> paths = Files.walk(Paths.get("./pipeline/defaultWebUIConfigs/"))) {
|
try (Stream<Path> paths =
|
||||||
|
Files.walk(
|
||||||
|
Paths.get(
|
||||||
|
InstallationPathConfig.getPipelineDefaultWebUIConfigsDir()))) {
|
||||||
List<Path> jsonFiles =
|
List<Path> jsonFiles =
|
||||||
paths.filter(Files::isRegularFile)
|
paths.filter(Files::isRegularFile)
|
||||||
.filter(p -> p.toString().endsWith(".json"))
|
.filter(p -> p.toString().endsWith(".json"))
|
||||||
|
|||||||
@@ -366,6 +366,7 @@ public class ApplicationProperties {
|
|||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
@ToString.Exclude private String key;
|
@ToString.Exclude private String key;
|
||||||
private int maxUsers;
|
private int maxUsers;
|
||||||
|
private boolean ssoAutoLogin;
|
||||||
private CustomMetadata customMetadata = new CustomMetadata();
|
private CustomMetadata customMetadata = new CustomMetadata();
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package stirling.software.SPDF.model.api.converters;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.api.PDFFile;
|
||||||
|
import stirling.software.SPDF.utils.PDFToFile;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Tag(name = "Convert", description = "Convert APIs")
|
||||||
|
@RequestMapping("/api/v1/convert")
|
||||||
|
public class ConvertPDFToMarkdown {
|
||||||
|
|
||||||
|
@PostMapping(consumes = "multipart/form-data", value = "/pdf/markdown")
|
||||||
|
@Operation(
|
||||||
|
summary = "Convert PDF to Markdown",
|
||||||
|
description =
|
||||||
|
"This endpoint converts a PDF file to Markdown format. Input:PDF Output:Markdown Type:SISO")
|
||||||
|
public ResponseEntity<byte[]> processPdfToMarkdown(@ModelAttribute PDFFile request)
|
||||||
|
throws Exception {
|
||||||
|
MultipartFile inputFile = request.getFileInput();
|
||||||
|
PDFToFile pdfToFile = new PDFToFile();
|
||||||
|
return pdfToFile.processPdfToMarkdown(inputFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ public class OptimizePdfRequest extends PDFFile {
|
|||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
description =
|
description =
|
||||||
"Whether to normalize the PDF content for better compatibility. Default is true.",
|
"Whether to normalize the PDF content for better compatibility. Default is false.",
|
||||||
defaultValue = "true")
|
defaultValue = "false")
|
||||||
private Boolean normalize = true;
|
private Boolean normalize = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package stirling.software.SPDF.model.api.security;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
package stirling.software.SPDF.model.api.security;
|
package stirling.software.SPDF.model.api.security;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class RedactionArea {
|
public class RedactionArea {
|
||||||
@Schema(description = "The left edge point of the area to be redacted.")
|
@Schema(description = "The left edge point of the area to be redacted.")
|
||||||
private Double x;
|
private Double x;
|
||||||
|
|
||||||
@Schema(description = "The top edge point of the area to be redacted.")
|
@Schema(description = "The top edge point of the area to be redacted.")
|
||||||
private Double y;
|
private Double y;
|
||||||
|
|
||||||
@Schema(description = "The height of the area to be redacted.")
|
@Schema(description = "The height of the area to be redacted.")
|
||||||
private Double height;
|
private Double height;
|
||||||
|
|
||||||
@Schema(description = "The width of the area to be redacted.")
|
@Schema(description = "The width of the area to be redacted.")
|
||||||
private Double width;
|
private Double width;
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class MetricsAggregatorService {
|
|||||||
if (method == null || uri == null) {
|
if (method == null || uri == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!method.equals("GET") && !method.equals("POST")) {
|
if (!"GET".equals(method) && !"POST".equals(method)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Skip URIs that are 2 characters or shorter
|
// Skip URIs that are 2 characters or shorter
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
|
||||||
|
import com.vladsch.flexmark.util.data.MutableDataSet;
|
||||||
|
|
||||||
import io.github.pixee.security.Filenames;
|
import io.github.pixee.security.Filenames;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -28,6 +31,123 @@ import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class PDFToFile {
|
public class PDFToFile {
|
||||||
|
|
||||||
|
public ResponseEntity<byte[]> processPdfToMarkdown(MultipartFile inputFile)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
if (!"application/pdf".equals(inputFile.getContentType())) {
|
||||||
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
MutableDataSet options =
|
||||||
|
new MutableDataSet()
|
||||||
|
.set(
|
||||||
|
FlexmarkHtmlConverter.MAX_BLANK_LINES,
|
||||||
|
2) // Control max consecutive blank lines
|
||||||
|
.set(
|
||||||
|
FlexmarkHtmlConverter.MAX_TRAILING_BLANK_LINES,
|
||||||
|
1) // Control trailing blank lines
|
||||||
|
.set(
|
||||||
|
FlexmarkHtmlConverter.SETEXT_HEADINGS,
|
||||||
|
true) // Use Setext headings for h1 and h2
|
||||||
|
.set(
|
||||||
|
FlexmarkHtmlConverter.OUTPUT_UNKNOWN_TAGS,
|
||||||
|
false) // Don't output HTML for unknown tags
|
||||||
|
.set(
|
||||||
|
FlexmarkHtmlConverter.TYPOGRAPHIC_QUOTES,
|
||||||
|
true) // Convert quotation marks
|
||||||
|
.set(
|
||||||
|
FlexmarkHtmlConverter.BR_AS_PARA_BREAKS,
|
||||||
|
true) // Convert <br> to paragraph breaks
|
||||||
|
.set(FlexmarkHtmlConverter.CODE_INDENT, " "); // Indent for code blocks
|
||||||
|
|
||||||
|
FlexmarkHtmlConverter htmlToMarkdownConverter =
|
||||||
|
FlexmarkHtmlConverter.builder(options).build();
|
||||||
|
|
||||||
|
String originalPdfFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
|
||||||
|
String pdfBaseName = originalPdfFileName;
|
||||||
|
if (originalPdfFileName.contains(".")) {
|
||||||
|
pdfBaseName = originalPdfFileName.substring(0, originalPdfFileName.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Path tempInputFile = null;
|
||||||
|
Path tempOutputDir = null;
|
||||||
|
byte[] fileBytes;
|
||||||
|
String fileName = "temp.file";
|
||||||
|
|
||||||
|
try {
|
||||||
|
tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||||
|
inputFile.transferTo(tempInputFile);
|
||||||
|
tempOutputDir = Files.createTempDirectory("output_");
|
||||||
|
|
||||||
|
List<String> command =
|
||||||
|
new ArrayList<>(
|
||||||
|
Arrays.asList(
|
||||||
|
"pdftohtml",
|
||||||
|
"-s",
|
||||||
|
"-noframes",
|
||||||
|
"-c",
|
||||||
|
tempInputFile.toString(),
|
||||||
|
pdfBaseName));
|
||||||
|
|
||||||
|
ProcessExecutorResult returnCode =
|
||||||
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.PDFTOHTML)
|
||||||
|
.runCommandWithOutputHandling(command, tempOutputDir.toFile());
|
||||||
|
// Process HTML files to Markdown
|
||||||
|
File[] outputFiles = Objects.requireNonNull(tempOutputDir.toFile().listFiles());
|
||||||
|
List<File> markdownFiles = new ArrayList<>();
|
||||||
|
|
||||||
|
// Convert HTML files to Markdown
|
||||||
|
for (File outputFile : outputFiles) {
|
||||||
|
if (outputFile.getName().endsWith(".html")) {
|
||||||
|
String html = Files.readString(outputFile.toPath());
|
||||||
|
String markdown = htmlToMarkdownConverter.convert(html);
|
||||||
|
|
||||||
|
String mdFileName = outputFile.getName().replace(".html", ".md");
|
||||||
|
File mdFile = new File(tempOutputDir.toFile(), mdFileName);
|
||||||
|
Files.writeString(mdFile.toPath(), markdown);
|
||||||
|
markdownFiles.add(mdFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's only one markdown file, return it directly
|
||||||
|
if (markdownFiles.size() == 1) {
|
||||||
|
fileName = pdfBaseName + ".md";
|
||||||
|
fileBytes = Files.readAllBytes(markdownFiles.get(0).toPath());
|
||||||
|
} else {
|
||||||
|
// Multiple files - create a zip
|
||||||
|
fileName = pdfBaseName + "ToMarkdown.zip";
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
|
||||||
|
// Add markdown files
|
||||||
|
for (File mdFile : markdownFiles) {
|
||||||
|
ZipEntry mdEntry = new ZipEntry(mdFile.getName());
|
||||||
|
zipOutputStream.putNextEntry(mdEntry);
|
||||||
|
Files.copy(mdFile.toPath(), zipOutputStream);
|
||||||
|
zipOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add images and other assets
|
||||||
|
for (File file : outputFiles) {
|
||||||
|
if (!file.getName().endsWith(".html") && !file.getName().endsWith(".md")) {
|
||||||
|
ZipEntry assetEntry = new ZipEntry(file.getName());
|
||||||
|
zipOutputStream.putNextEntry(assetEntry);
|
||||||
|
Files.copy(file.toPath(), zipOutputStream);
|
||||||
|
zipOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileBytes = byteArrayOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (tempInputFile != null) Files.deleteIfExists(tempInputFile);
|
||||||
|
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
|
||||||
|
}
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
}
|
||||||
|
|
||||||
public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile)
|
public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
if (!"application/pdf".equals(inputFile.getContentType())) {
|
if (!"application/pdf".equals(inputFile.getContentType())) {
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ public class StringToArrayListPropertyEditor extends PropertyEditorSupport {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||||
TypeReference<ArrayList<RedactionArea>> typeRef = new TypeReference<ArrayList<RedactionArea>>() {
|
TypeReference<ArrayList<RedactionArea>> typeRef =
|
||||||
};
|
new TypeReference<ArrayList<RedactionArea>>() {};
|
||||||
List<RedactionArea> list = objectMapper.readValue(text, typeRef);
|
List<RedactionArea> list = objectMapper.readValue(text, typeRef);
|
||||||
setValue(list);
|
setValue(list);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown إلى PDF
|
|||||||
home.MarkdownToPDF.desc=يحول أي ملف Markdown إلى PDF
|
home.MarkdownToPDF.desc=يحول أي ملف Markdown إلى PDF
|
||||||
MarkdownToPDF.tags=لغة الترميز,محتوى الويب,تحويل,تحويل
|
MarkdownToPDF.tags=لغة الترميز,محتوى الويب,تحويل,تحويل
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=الحصول على جميع المعلومات عن PDF
|
home.getPdfInfo.title=الحصول على جميع المعلومات عن PDF
|
||||||
home.getPdfInfo.desc=يجمع أي وكل المعلومات الممكنة عن ملفات PDF
|
home.getPdfInfo.desc=يجمع أي وكل المعلومات الممكنة عن ملفات PDF
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=العمل قيد التقدم
|
|||||||
MarkdownToPDF.credit=يستخدم WeasyPrint
|
MarkdownToPDF.credit=يستخدم WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL إلى PDF
|
URLToPDF.title=URL إلى PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=تحويل إلى PDF
|
|||||||
compress.title=ضغط
|
compress.title=ضغط
|
||||||
compress.header=ضغط ملف PDF
|
compress.header=ضغط ملف PDF
|
||||||
compress.credit=تستخدم هذه الخدمة qpdf لضغط / تحسين PDF.
|
compress.credit=تستخدم هذه الخدمة qpdf لضغط / تحسين PDF.
|
||||||
compress.selectText.1=الوضع اليدوي - من 1 إلى 4
|
compress.selectText.1=الوضع اليدوي - من 1 إلى 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=مستوى التحسين:
|
compress.selectText.2=مستوى التحسين:
|
||||||
compress.selectText.3=4 (رهيب للصور النصية)
|
compress.selectText.3=4 (رهيب للصور النصية)
|
||||||
compress.selectText.4=الوضع التلقائي - يضبط الجودة تلقائيًا للحصول على ملف PDF بالحجم المحدد
|
compress.selectText.4=الوضع التلقائي - يضبط الجودة تلقائيًا للحصول على ملف PDF بالحجم المحدد
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown-dan PDF-ə
|
|||||||
home.MarkdownToPDF.desc=Hər hansı Markdown faylını PDF-ə çevirir
|
home.MarkdownToPDF.desc=Hər hansı Markdown faylını PDF-ə çevirir
|
||||||
MarkdownToPDF.tags=işarələmə,web-məzmun,dəyişmə,çevirmə
|
MarkdownToPDF.tags=işarələmə,web-məzmun,dəyişmə,çevirmə
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=PDF-in Bütün Məlumatları
|
home.getPdfInfo.title=PDF-in Bütün Məlumatları
|
||||||
home.getPdfInfo.desc=PDF barədə mümkün olan bütün məlumatları əldə edir
|
home.getPdfInfo.desc=PDF barədə mümkün olan bütün məlumatları əldə edir
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=İş davam edir
|
|||||||
MarkdownToPDF.credit=WeasyPrint İstifadə Edir
|
MarkdownToPDF.credit=WeasyPrint İstifadə Edir
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL-i PDF-ə
|
URLToPDF.title=URL-i PDF-ə
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=PDF-ə Çevir
|
|||||||
compress.title=Sıxışdır
|
compress.title=Sıxışdır
|
||||||
compress.header=PDF-i Sıxışdır
|
compress.header=PDF-i Sıxışdır
|
||||||
compress.credit=Bu servis PDF sıxışdırılması/Optimizasiyası üçün Ghostscript istifadə edir.
|
compress.credit=Bu servis PDF sıxışdırılması/Optimizasiyası üçün Ghostscript istifadə edir.
|
||||||
compress.selectText.1=Manual Mod - 1-dən 4-ə
|
compress.selectText.1=Manual Mod - 1-dən 5-ə
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Optimizasiya səviyyəsi:
|
compress.selectText.2=Optimizasiya səviyyəsi:
|
||||||
compress.selectText.3=4 (Mətn şəkilləri üçün yaxşı deyil)
|
compress.selectText.3=4 (Mətn şəkilləri üçün yaxşı deyil)
|
||||||
compress.selectText.4=Avto mod - PDF-in dəqiq ölçüsünü əldə etmək üçün keyfiyyəti avtomatik tənzimləyir
|
compress.selectText.4=Avto mod - PDF-in dəqiq ölçüsünü əldə etmək üçün keyfiyyəti avtomatik tənzimləyir
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown към PDF
|
|||||||
home.MarkdownToPDF.desc=Преобразува всеки Markdown файл към PDF
|
home.MarkdownToPDF.desc=Преобразува всеки Markdown файл към PDF
|
||||||
MarkdownToPDF.tags=маркиране,уеб-съдържание,трансформация,преобразуване
|
MarkdownToPDF.tags=маркиране,уеб-съдържание,трансформация,преобразуване
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Вземете ЦЯЛАТА информация от PDF
|
home.getPdfInfo.title=Вземете ЦЯЛАТА информация от PDF
|
||||||
home.getPdfInfo.desc=Взима всяка възможна информация от PDF файлове
|
home.getPdfInfo.desc=Взима всяка възможна информация от PDF файлове
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Работата е в ход
|
|||||||
MarkdownToPDF.credit=Използва WeasyPrint
|
MarkdownToPDF.credit=Използва WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL към PDF
|
URLToPDF.title=URL към PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Преобразуване към PDF
|
|||||||
compress.title=Компресиране
|
compress.title=Компресиране
|
||||||
compress.header=Компресиране на PDF
|
compress.header=Компресиране на PDF
|
||||||
compress.credit=Тази услуга използва qpdf за PDF компресиране/оптимизиране.
|
compress.credit=Тази услуга използва qpdf за PDF компресиране/оптимизиране.
|
||||||
compress.selectText.1=Ръчен режим - от 1 до 4
|
compress.selectText.1=Ръчен режим - от 1 до 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Ниво на оптимизация:
|
compress.selectText.2=Ниво на оптимизация:
|
||||||
compress.selectText.3=4 (Ужасно за текстови изображения)
|
compress.selectText.3=4 (Ужасно за текстови изображения)
|
||||||
compress.selectText.4=Автоматичен режим - Автоматично настройва качеството, за да получи PDF с точен размер
|
compress.selectText.4=Автоматичен режим - Автоматично настройва качеството, за да получи PDF с точен размер
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown a PDF
|
|||||||
home.MarkdownToPDF.desc=Converteix qualsevol fitxer Markdown a PDF
|
home.MarkdownToPDF.desc=Converteix qualsevol fitxer Markdown a PDF
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Obteniu Tota la Informació sobre el PDF
|
home.getPdfInfo.title=Obteniu Tota la Informació sobre el PDF
|
||||||
home.getPdfInfo.desc=Recupera tota la informació possible sobre els PDFs
|
home.getPdfInfo.desc=Recupera tota la informació possible sobre els PDFs
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Work in progress
|
|||||||
MarkdownToPDF.credit=Uses WeasyPrint
|
MarkdownToPDF.credit=Uses WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL a PDF
|
URLToPDF.title=URL a PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Converteix a PDF
|
|||||||
compress.title=Comprimir
|
compress.title=Comprimir
|
||||||
compress.header=Comprimir PDF
|
compress.header=Comprimir PDF
|
||||||
compress.credit=Aquest servei utilitza qpdf 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.1=Mode manual: de l'1 al 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Nivell d'optimització:
|
compress.selectText.2=Nivell d'optimització:
|
||||||
compress.selectText.3=4 (terrible per a imatges de text)
|
compress.selectText.3=4 (terrible per a imatges de text)
|
||||||
compress.selectText.4=Mode automàtic: ajusta automàticament la qualitat perquè el PDF tingui la mida exacta
|
compress.selectText.4=Mode automàtic: ajusta automàticament la qualitat perquè el PDF tingui la mida exacta
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown til PDF
|
|||||||
home.MarkdownToPDF.desc=Konverterer enhver Markdown-fil til PDF
|
home.MarkdownToPDF.desc=Konverterer enhver Markdown-fil til PDF
|
||||||
MarkdownToPDF.tags=markup,webindhold,transformation,konvertér
|
MarkdownToPDF.tags=markup,webindhold,transformation,konvertér
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Få ALLE Oplysninger om PDF
|
home.getPdfInfo.title=Få ALLE Oplysninger om PDF
|
||||||
home.getPdfInfo.desc=Henter alle mulige oplysninger om PDF'er
|
home.getPdfInfo.desc=Henter alle mulige oplysninger om PDF'er
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Arbejde i gang
|
|||||||
MarkdownToPDF.credit=Bruger WeasyPrint
|
MarkdownToPDF.credit=Bruger WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL Til PDF
|
URLToPDF.title=URL Til PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Konvertér til PDF
|
|||||||
compress.title=Komprimer
|
compress.title=Komprimer
|
||||||
compress.header=Komprimer PDF
|
compress.header=Komprimer PDF
|
||||||
compress.credit=Denne tjeneste bruger qpdf 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.1=Manuel Tilstand - Fra 1 til 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Optimeringsniveau:
|
compress.selectText.2=Optimeringsniveau:
|
||||||
compress.selectText.3=4 (Forfærdelig for tekstbilleder)
|
compress.selectText.3=4 (Forfærdelig for tekstbilleder)
|
||||||
compress.selectText.4=Auto tilstand - Justerer automatisk kvaliteten for at få PDF'en til en præcis størrelse
|
compress.selectText.4=Auto tilstand - Justerer automatisk kvaliteten for at få PDF'en til en præcis størrelse
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ pages=Seiten
|
|||||||
loading=Laden...
|
loading=Laden...
|
||||||
addToDoc=In Dokument hinzufügen
|
addToDoc=In Dokument hinzufügen
|
||||||
reset=Zurücksetzen
|
reset=Zurücksetzen
|
||||||
apply=Apply
|
apply=Anwenden
|
||||||
|
|
||||||
legal.privacy=Datenschutz
|
legal.privacy=Datenschutz
|
||||||
legal.terms=AGB
|
legal.terms=AGB
|
||||||
@@ -249,7 +249,7 @@ database.backupCreated=Datenbanksicherung erfolgreich
|
|||||||
database.fileNotFound=Datei nicht gefunden
|
database.fileNotFound=Datei nicht gefunden
|
||||||
database.fileNullOrEmpty=Datei darf nicht null oder leer sein
|
database.fileNullOrEmpty=Datei darf nicht null oder leer sein
|
||||||
database.failedImportFile=Dateiimport fehlgeschlagen
|
database.failedImportFile=Dateiimport fehlgeschlagen
|
||||||
database.notSupported=This function is not available for your database connection.
|
database.notSupported=Diese Funktion ist für deine Datenbankverbindung nicht verfügbar.
|
||||||
|
|
||||||
session.expired=Ihre Sitzung ist abgelaufen. Bitte laden Sie die Seite neu und versuchen Sie es erneut.
|
session.expired=Ihre Sitzung ist abgelaufen. Bitte laden Sie die Seite neu und versuchen Sie es erneut.
|
||||||
session.refreshPage=Seite aktualisieren
|
session.refreshPage=Seite aktualisieren
|
||||||
@@ -271,7 +271,7 @@ multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side
|
|||||||
|
|
||||||
home.merge.title=Zusammenführen
|
home.merge.title=Zusammenführen
|
||||||
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen
|
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen
|
||||||
merge.tags=zusammenführen,seitenvorgänge,back end,serverseite
|
merge.tags=zusammenführen,seitenvorgänge,back end,serverseitig
|
||||||
|
|
||||||
home.split.title=Aufteilen
|
home.split.title=Aufteilen
|
||||||
home.split.desc=PDFs in mehrere Dokumente aufteilen
|
home.split.desc=PDFs in mehrere Dokumente aufteilen
|
||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown zu PDF
|
|||||||
home.MarkdownToPDF.desc=Konvertiert jede Markdown-Datei zu PDF
|
home.MarkdownToPDF.desc=Konvertiert jede Markdown-Datei zu PDF
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,konvertieren
|
MarkdownToPDF.tags=markup,web-content,transformation,konvertieren
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF zu Markdown
|
||||||
|
home.PDFToMarkdown.desc=Konvertiert jedes PDF in Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web inhalt,transformation,konvertieren,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Alle Informationen anzeigen
|
home.getPdfInfo.title=Alle Informationen anzeigen
|
||||||
home.getPdfInfo.desc=Erfasst alle möglichen Informationen in einer PDF
|
home.getPdfInfo.desc=Erfasst alle möglichen Informationen in einer PDF
|
||||||
@@ -476,9 +479,9 @@ home.autoRedact.title=Automatisch zensieren/schwärzen
|
|||||||
home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text
|
home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text
|
||||||
autoRedact.tags=zensieren,schwärzen
|
autoRedact.tags=zensieren,schwärzen
|
||||||
|
|
||||||
home.redact.title=Manual Redaction
|
home.redact.title=Manuell zensieren/schwärzen
|
||||||
home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s)
|
home.redact.desc=Zensiere (Schwärze) eine PDF-Datei durch Auswählen von Text, gezeichneten Formen und/oder ausgewählten Seite(n)
|
||||||
redact.tags=Redact,Hide,black out,black,marker,hidden,manual
|
redact.tags=zensieren,schwärzen,verstecken,verdunkeln,schwarz,markieren,verbergen,manuell
|
||||||
|
|
||||||
home.tableExtraxt.title=Tabelle extrahieren
|
home.tableExtraxt.title=Tabelle extrahieren
|
||||||
home.tableExtraxt.desc=Tabelle aus PDF in CSV extrahieren
|
home.tableExtraxt.desc=Tabelle aus PDF in CSV extrahieren
|
||||||
@@ -586,30 +589,30 @@ autoRedact.convertPDFToImageLabel=PDF in PDF-Bild konvertieren (zum Entfernen vo
|
|||||||
autoRedact.submitButton=Zensieren
|
autoRedact.submitButton=Zensieren
|
||||||
|
|
||||||
#redact
|
#redact
|
||||||
redact.title=Manual Redaction
|
redact.title=Manuelles Zensieren (Schwärzen)
|
||||||
redact.header=Manual Redaction
|
redact.header=Manuelles Zensieren (Schwärzen)
|
||||||
redact.submit=Redact
|
redact.submit=Zensieren
|
||||||
redact.textBasedRedaction=Text based Redaction
|
redact.textBasedRedaction=Textbasiertes Zensieren
|
||||||
redact.pageBasedRedaction=Page-based Redaction
|
redact.pageBasedRedaction=Seitenweises Zensieren
|
||||||
redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box)
|
redact.convertPDFToImageLabel=Konvertiere PDF zu einem Bild (Zum Entfernen von Text hinter der Box verwenden)
|
||||||
redact.pageRedactionNumbers.title=Pages
|
redact.pageRedactionNumbers.title=Seiten
|
||||||
redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
redact.pageRedactionNumbers.placeholder=(z.B. 1,2,8 oder 4,7,12-16 oder 2n-1)
|
||||||
redact.redactionColor.title=Redaction Color
|
redact.redactionColor.title=Zensurfarbe
|
||||||
redact.export=Export
|
redact.export=Exportieren
|
||||||
redact.upload=Upload
|
redact.upload=Hochladen
|
||||||
redact.boxRedaction=Box draw redaction
|
redact.boxRedaction=Rechteck zeichnen zum zensieren
|
||||||
redact.zoom=Zoom
|
redact.zoom=Zoom
|
||||||
redact.zoomIn=Zoom in
|
redact.zoomIn=Vergrößern
|
||||||
redact.zoomOut=Zoom out
|
redact.zoomOut=Verkleinern
|
||||||
redact.nextPage=Next Page
|
redact.nextPage=Nächste Seite
|
||||||
redact.previousPage=Previous Page
|
redact.previousPage=Vorherige Seite
|
||||||
redact.toggleSidebar=Toggle Sidebar
|
redact.toggleSidebar=Seitenleiste umschalten
|
||||||
redact.showThumbnails=Show Thumbnails
|
redact.showThumbnails=Vorschau anzeigen
|
||||||
redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items)
|
redact.showDocumentOutline=Dokumentübersicht anzeigen (Doppelklick zum Auf/Einklappen aller Elemente)
|
||||||
redact.showAttatchments=Show Attachments
|
redact.showAttatchments=Zeige Anhänge
|
||||||
redact.showLayers=Show Layers (double-click to reset all layers to the default state)
|
redact.showLayers=Ebenen anzeigen (Doppelklick, um alle Ebenen auf den Standardzustand zurückzusetzen)
|
||||||
redact.colourPicker=Colour Picker
|
redact.colourPicker=Farbauswahl
|
||||||
redact.findCurrentOutlineItem=Find current outline item
|
redact.findCurrentOutlineItem=Aktuell gewähltes Element finden
|
||||||
|
|
||||||
#showJS
|
#showJS
|
||||||
showJS.title=Javascript anzeigen
|
showJS.title=Javascript anzeigen
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=In Arbeit
|
|||||||
MarkdownToPDF.credit=Verwendet WeasyPrint
|
MarkdownToPDF.credit=Verwendet WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF zu Markdown
|
||||||
|
PDFToMarkdown.header=PDF zu Markdown
|
||||||
|
PDFToMarkdown.submit=Konvertieren
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL zu PDF
|
URLToPDF.title=URL zu PDF
|
||||||
@@ -862,7 +870,7 @@ sign.first=Erste Seite
|
|||||||
sign.last=Letzte Seite
|
sign.last=Letzte Seite
|
||||||
sign.next=Nächste Seite
|
sign.next=Nächste Seite
|
||||||
sign.previous=Vorherige Seite
|
sign.previous=Vorherige Seite
|
||||||
sign.maintainRatio=Toggle maintain aspect ratio
|
sign.maintainRatio=Seitenverhältnis beibehalten ein-/ausschalten
|
||||||
|
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=In PDF konvertieren
|
|||||||
compress.title=Komprimieren
|
compress.title=Komprimieren
|
||||||
compress.header=PDF komprimieren
|
compress.header=PDF komprimieren
|
||||||
compress.credit=Dieser Dienst verwendet qpdf 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.1=Manueller Modus – Von 1 bis 5
|
||||||
|
compress.selectText.1.1=In den Optimierungsstufen 6 bis 9 wird zusätzlich zur allgemeinen PDF-Komprimierung die Bildauflösung reduziert, um die Dateigröße weiter zu verringern. Höhere Stufen führen zu einer stärkeren Bildkomprimierung (bis zu 50 % der Originalgröße), wodurch eine stärkere Größenreduzierung erreicht wird, die jedoch mit einem möglichen Qualitätsverlust der Bilder einhergeht.
|
||||||
compress.selectText.2=Optimierungsstufe:
|
compress.selectText.2=Optimierungsstufe:
|
||||||
compress.selectText.3=4 (Schrecklich für Textbilder)
|
compress.selectText.3=4 (Schrecklich für Textbilder)
|
||||||
compress.selectText.4=Automatischer Modus – Passt die Qualität automatisch an, um das PDF auf die exakte Größe zu bringen
|
compress.selectText.4=Automatischer Modus – Passt die Qualität automatisch an, um das PDF auf die exakte Größe zu bringen
|
||||||
@@ -1319,8 +1328,8 @@ splitByChapters.submit=PDF teilen
|
|||||||
fileChooser.click=Klicken
|
fileChooser.click=Klicken
|
||||||
fileChooser.or=oder
|
fileChooser.or=oder
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
fileChooser.dragAndDropPDF=Drag & Drop PDF-Datei
|
||||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
fileChooser.dragAndDropImage=Drag & Drop Bilddatei
|
||||||
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -450,8 +450,11 @@ HTMLToPDF.tags=markup,web-content,transformation,convert
|
|||||||
|
|
||||||
home.MarkdownToPDF.title=Markdown to PDF
|
home.MarkdownToPDF.title=Markdown to PDF
|
||||||
home.MarkdownToPDF.desc=Converts any Markdown file to PDF
|
home.MarkdownToPDF.desc=Converts any Markdown file to PDF
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
MarkdownToPDF.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Get ALL Info on PDF
|
home.getPdfInfo.title=Get ALL Info on PDF
|
||||||
home.getPdfInfo.desc=Grabs any and all information possible on PDFs
|
home.getPdfInfo.desc=Grabs any and all information possible on PDFs
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Work in progress
|
|||||||
MarkdownToPDF.credit=Uses WeasyPrint
|
MarkdownToPDF.credit=Uses WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL To PDF
|
URLToPDF.title=URL To PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Convert to PDF
|
|||||||
compress.title=Compress
|
compress.title=Compress
|
||||||
compress.header=Compress PDF
|
compress.header=Compress PDF
|
||||||
compress.credit=This service uses qpdf 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.1=Manual Mode - From 1 to 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Optimisation level:
|
compress.selectText.2=Optimisation level:
|
||||||
compress.selectText.3=4 (Terrible for text images)
|
compress.selectText.3=4 (Terrible for text images)
|
||||||
compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
|
compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
|
||||||
|
|||||||
@@ -450,8 +450,11 @@ HTMLToPDF.tags=markup,web-content,transformation,convert
|
|||||||
|
|
||||||
home.MarkdownToPDF.title=Markdown to PDF
|
home.MarkdownToPDF.title=Markdown to PDF
|
||||||
home.MarkdownToPDF.desc=Converts any Markdown file to PDF
|
home.MarkdownToPDF.desc=Converts any Markdown file to PDF
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
MarkdownToPDF.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Get ALL Info on PDF
|
home.getPdfInfo.title=Get ALL Info on PDF
|
||||||
home.getPdfInfo.desc=Grabs any and all information possible on PDFs
|
home.getPdfInfo.desc=Grabs any and all information possible on PDFs
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Work in progress
|
|||||||
MarkdownToPDF.credit=Uses WeasyPrint
|
MarkdownToPDF.credit=Uses WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL To PDF
|
URLToPDF.title=URL To PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Convert to PDF
|
|||||||
compress.title=Compress
|
compress.title=Compress
|
||||||
compress.header=Compress PDF
|
compress.header=Compress PDF
|
||||||
compress.credit=This service uses qpdf 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.1=Manual Mode - From 1 to 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Optimization level:
|
compress.selectText.2=Optimization level:
|
||||||
compress.selectText.3=4 (Terrible for text images)
|
compress.selectText.3=4 (Terrible for text images)
|
||||||
compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
|
compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown a PDF
|
|||||||
home.MarkdownToPDF.desc=Convierte cualquier archivo Markdown a PDF
|
home.MarkdownToPDF.desc=Convierte cualquier archivo Markdown a PDF
|
||||||
MarkdownToPDF.tags=margen,contenido web,transformación,convertir
|
MarkdownToPDF.tags=margen,contenido web,transformación,convertir
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Obtener toda la información en PDF
|
home.getPdfInfo.title=Obtener toda la información en PDF
|
||||||
home.getPdfInfo.desc=Obtiene toda la información posible de archivos PDF
|
home.getPdfInfo.desc=Obtiene toda la información posible de archivos PDF
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Tarea en proceso
|
|||||||
MarkdownToPDF.credit=Usa WeasyPrint
|
MarkdownToPDF.credit=Usa WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL a PDF
|
URLToPDF.title=URL a PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Convertir a PDF
|
|||||||
compress.title=Comprimir
|
compress.title=Comprimir
|
||||||
compress.header=Comprimir PDF
|
compress.header=Comprimir PDF
|
||||||
compress.credit=Este servicio utiliza qpdf 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.1=Modo manual - De 1 a 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Nivel de optimización:
|
compress.selectText.2=Nivel de optimización:
|
||||||
compress.selectText.3=4 (Terrible para imágenes de texto)
|
compress.selectText.3=4 (Terrible para imágenes de texto)
|
||||||
compress.selectText.4=Modo automático: ajusta automáticamente la calidad para que el PDF tenga el tamaño exacto
|
compress.selectText.4=Modo automático: ajusta automáticamente la calidad para que el PDF tenga el tamaño exacto
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown PDF-ra
|
|||||||
home.MarkdownToPDF.desc=Bihurtu Markdown fitxategi guztiak PDF
|
home.MarkdownToPDF.desc=Bihurtu Markdown fitxategi guztiak PDF
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Lortu informazio guztia PDF-tik
|
home.getPdfInfo.title=Lortu informazio guztia PDF-tik
|
||||||
home.getPdfInfo.desc=Eskuratu PDF fitxategiko Informazio guztia
|
home.getPdfInfo.desc=Eskuratu PDF fitxategiko Informazio guztia
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Lanean
|
|||||||
MarkdownToPDF.credit=WeasyPrint darabil
|
MarkdownToPDF.credit=WeasyPrint darabil
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL bat PDF-ra
|
URLToPDF.title=URL bat PDF-ra
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=PDF bihurtu
|
|||||||
compress.title=Konprimatu
|
compress.title=Konprimatu
|
||||||
compress.header=PDFa konprimatu
|
compress.header=PDFa konprimatu
|
||||||
compress.credit=Zerbitzu honek qpdf erabiltzen du PDFak komprimatzeko/optimizatzeko
|
compress.credit=Zerbitzu honek qpdf erabiltzen du PDFak komprimatzeko/optimizatzeko
|
||||||
compress.selectText.1=Eskuz 1etik 4ra
|
compress.selectText.1=Eskuz 1etik 5ra
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Optimizazio maila:
|
compress.selectText.2=Optimizazio maila:
|
||||||
compress.selectText.3=4 (Izugarria testu-irudietarako)
|
compress.selectText.3=4 (Izugarria testu-irudietarako)
|
||||||
compress.selectText.4=Automatikoa: automatikoki egokitzen du kalitatea PDFak tamaina doi-doia izan dezan
|
compress.selectText.4=Automatikoa: automatikoki egokitzen du kalitatea PDFak tamaina doi-doia izan dezan
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=مارکداون به PDF
|
|||||||
home.MarkdownToPDF.desc=تبدیل هر فایل مارکداون به PDF
|
home.MarkdownToPDF.desc=تبدیل هر فایل مارکداون به PDF
|
||||||
MarkdownToPDF.tags=مارکآپ،محتوای وب،تبدیل،تغییر
|
MarkdownToPDF.tags=مارکآپ،محتوای وب،تبدیل،تغییر
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=دریافت تمام اطلاعات در مورد PDF
|
home.getPdfInfo.title=دریافت تمام اطلاعات در مورد PDF
|
||||||
home.getPdfInfo.desc=گرفتن هر اطلاعات ممکن در مورد PDF
|
home.getPdfInfo.desc=گرفتن هر اطلاعات ممکن در مورد PDF
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=در حال پیشرفت
|
|||||||
MarkdownToPDF.credit=از WeasyPrint استفاده میکند
|
MarkdownToPDF.credit=از WeasyPrint استفاده میکند
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL به PDF
|
URLToPDF.title=URL به PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=تبدیل به PDF
|
|||||||
compress.title=فشردهسازی
|
compress.title=فشردهسازی
|
||||||
compress.header=فشردهسازی PDF
|
compress.header=فشردهسازی PDF
|
||||||
compress.credit=این سرویس از qpdf برای فشردهسازی / بهینهسازی PDF استفاده میکند.
|
compress.credit=این سرویس از qpdf برای فشردهسازی / بهینهسازی PDF استفاده میکند.
|
||||||
compress.selectText.1=حالت دستی - از ۱ تا ۴
|
compress.selectText.1=حالت دستی - از 1 تا 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=سطح بهینهسازی:
|
compress.selectText.2=سطح بهینهسازی:
|
||||||
compress.selectText.3=۴ (خیلی بد برای تصاویر متنی)
|
compress.selectText.3=۴ (خیلی بد برای تصاویر متنی)
|
||||||
compress.selectText.4=حالت خودکار - کیفیت را به طور خودکار تنظیم میکند تا PDF به اندازه دقیق برسد
|
compress.selectText.4=حالت خودکار - کیفیت را به طور خودکار تنظیم میکند تا PDF به اندازه دقیق برسد
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ pages=Pages
|
|||||||
loading=Chargement...
|
loading=Chargement...
|
||||||
addToDoc=Ajouter au Document
|
addToDoc=Ajouter au Document
|
||||||
reset=Réinitialiser
|
reset=Réinitialiser
|
||||||
apply=Apply
|
apply=Appliquer
|
||||||
|
|
||||||
legal.privacy=Politique de Confidentialité
|
legal.privacy=Politique de Confidentialité
|
||||||
legal.terms=Conditions Générales
|
legal.terms=Conditions Générales
|
||||||
@@ -239,20 +239,20 @@ database.creationDate=Date de Création
|
|||||||
database.fileSize=Taille du Fichier
|
database.fileSize=Taille du Fichier
|
||||||
database.deleteBackupFile=Supprimer le fichier de sauvegarde
|
database.deleteBackupFile=Supprimer le fichier de sauvegarde
|
||||||
database.importBackupFile=Importer le fichier de sauvegarde
|
database.importBackupFile=Importer le fichier de sauvegarde
|
||||||
database.createBackupFile=Create Backup File
|
database.createBackupFile=Créer un fichier de sauvegarde
|
||||||
database.downloadBackupFile=Télécharger le fichier de sauvegarde
|
database.downloadBackupFile=Télécharger le fichier de sauvegarde
|
||||||
database.info_1=Lors de l'importation des données, il est crucial de garantir la structure correcte. Si vous n'êtes pas sûr de ce que vous faites, sollicitez un avis et un soutien d'un professionnel. Une erreur dans la structure peut entraîner des dysfonctionnements de l'application, allant jusqu'à l'incapacité totale d'exécuter l'application.
|
database.info_1=Lors de l'importation des données, il est crucial de garantir la structure correcte. Si vous n'êtes pas sûr de ce que vous faites, sollicitez un avis et un soutien d'un professionnel. Une erreur dans la structure peut entraîner des dysfonctionnements de l'application, allant jusqu'à l'incapacité totale d'exécuter l'application.
|
||||||
database.info_2=Le nom du fichier ne fait pas de différence lors de l'upload. Il sera renommé ultérieurement selon le format backup_user_yyyyMMddHHmm.sql, assurant ainsi une convention de nommage cohérente.
|
database.info_2=Le nom du fichier ne fait pas de différence lors de l'upload. Il sera renommé ultérieurement selon le format backup_user_yyyyMMddHHmm.sql, assurant ainsi une convention de nommage cohérente.
|
||||||
database.submit=Importer la sauvegarde
|
database.submit=Importer la sauvegarde
|
||||||
database.importIntoDatabaseSuccessed=Importation dans la base de données réussie
|
database.importIntoDatabaseSuccessed=Importation dans la base de données réussie
|
||||||
database.backupCreated=Database backup successful
|
database.backupCreated=Sauvegarde de la base de donnée réussie
|
||||||
database.fileNotFound=File not Found
|
database.fileNotFound=Fichier introuvable
|
||||||
database.fileNullOrEmpty=Fichier ne peut pas être null ou vide
|
database.fileNullOrEmpty=Fichier ne peut pas être null ou vide
|
||||||
database.failedImportFile=Failed Import File
|
database.failedImportFile=Échec de l'imporation du fichier
|
||||||
database.notSupported=This function is not available for your database connection.
|
database.notSupported=Cette fonctionnalité n'est pas supportée avec votre base de donnée
|
||||||
|
|
||||||
session.expired=Votre session a expiré. Veuillez recharger la page et réessayer.
|
session.expired=Votre session a expiré. Veuillez recharger la page et réessayer.
|
||||||
session.refreshPage=Refresh Page
|
session.refreshPage=Rafraichir la page
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown en PDF
|
|||||||
home.MarkdownToPDF.desc=Convertissez n'importe quel fichier Markdown en PDF.
|
home.MarkdownToPDF.desc=Convertissez n'importe quel fichier Markdown en PDF.
|
||||||
MarkdownToPDF.tags=markdown,markup,contenu Web,transformation,convert
|
MarkdownToPDF.tags=markdown,markup,contenu Web,transformation,convert
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Récupérer les informations
|
home.getPdfInfo.title=Récupérer les informations
|
||||||
home.getPdfInfo.desc=Récupérez toutes les informations possibles sur un PDF.
|
home.getPdfInfo.desc=Récupérez toutes les informations possibles sur un PDF.
|
||||||
@@ -476,8 +479,8 @@ home.autoRedact.title=Caviarder automatiquement
|
|||||||
home.autoRedact.desc=Caviardez automatiquement les informations sensibles d'un PDF.
|
home.autoRedact.desc=Caviardez automatiquement les informations sensibles d'un PDF.
|
||||||
autoRedact.tags=caviarder,redact,auto
|
autoRedact.tags=caviarder,redact,auto
|
||||||
|
|
||||||
home.redact.title=Manual Redaction
|
home.redact.title=Rédaction manuelle
|
||||||
home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s)
|
home.redact.desc=Rédiger un PDF en fonction de texte sélectionné, formes dessinées et/ou des pages sélectionnées.
|
||||||
redact.tags=Redact,Hide,black out,black,marker,hidden,manual
|
redact.tags=Redact,Hide,black out,black,marker,hidden,manual
|
||||||
|
|
||||||
home.tableExtraxt.title=PDF en CSV
|
home.tableExtraxt.title=PDF en CSV
|
||||||
@@ -586,30 +589,30 @@ autoRedact.convertPDFToImageLabel=Convertir un PDF en PDF-Image (utilisé pour s
|
|||||||
autoRedact.submitButton=Caviarder
|
autoRedact.submitButton=Caviarder
|
||||||
|
|
||||||
#redact
|
#redact
|
||||||
redact.title=Manual Redaction
|
redact.title=Rédaction manuelle
|
||||||
redact.header=Manual Redaction
|
redact.header=Rédaction manuelle
|
||||||
redact.submit=Redact
|
redact.submit=Rédiger
|
||||||
redact.textBasedRedaction=Text based Redaction
|
redact.textBasedRedaction=Rédaction en fonction de texte
|
||||||
redact.pageBasedRedaction=Page-based Redaction
|
redact.pageBasedRedaction=Rédaction en fonction de pages
|
||||||
redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box)
|
redact.convertPDFToImageLabel=Convertir en PDF-Image (pour supprimer le texte derrière le rectangle)
|
||||||
redact.pageRedactionNumbers.title=Pages
|
redact.pageRedactionNumbers.title=Pages
|
||||||
redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
redact.pageRedactionNumbers.placeholder=(ex: 1,2,8 ou 4,7,12-16 ou 2n-1)
|
||||||
redact.redactionColor.title=Redaction Color
|
redact.redactionColor.title=Couleur
|
||||||
redact.export=Export
|
redact.export=Exporter
|
||||||
redact.upload=Upload
|
redact.upload=Téléverser
|
||||||
redact.boxRedaction=Box draw redaction
|
redact.boxRedaction=Dessiner le rectangle à rédiger
|
||||||
redact.zoom=Zoom
|
redact.zoom=Zoom
|
||||||
redact.zoomIn=Zoom in
|
redact.zoomIn=Zoom avant
|
||||||
redact.zoomOut=Zoom out
|
redact.zoomOut=Zoom arrière
|
||||||
redact.nextPage=Next Page
|
redact.nextPage=Page suivante
|
||||||
redact.previousPage=Previous Page
|
redact.previousPage=Page précédente
|
||||||
redact.toggleSidebar=Toggle Sidebar
|
redact.toggleSidebar=Toggle Sidebar
|
||||||
redact.showThumbnails=Show Thumbnails
|
redact.showThumbnails=Afficher les miniatures
|
||||||
redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items)
|
redact.showDocumentOutline=Montrer les contours du document (double-click pour agrandir/réduire tous les éléments)
|
||||||
redact.showAttatchments=Show Attachments
|
redact.showAttatchments=Montrer les éléments attachés
|
||||||
redact.showLayers=Show Layers (double-click to reset all layers to the default state)
|
redact.showLayers=Montrer les calques (double-click pour réinitialiser tous les calques à l'état par défaut)
|
||||||
redact.colourPicker=Colour Picker
|
redact.colourPicker=Sélection de couleur
|
||||||
redact.findCurrentOutlineItem=Find current outline item
|
redact.findCurrentOutlineItem=Trouver l'élément de contour courrant
|
||||||
|
|
||||||
#showJS
|
#showJS
|
||||||
showJS.title=Afficher le JavaScript
|
showJS.title=Afficher le JavaScript
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=(Travail en cours).
|
|||||||
MarkdownToPDF.credit=Utilise WeasyPrint.
|
MarkdownToPDF.credit=Utilise WeasyPrint.
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL en PDF
|
URLToPDF.title=URL en PDF
|
||||||
@@ -856,13 +864,13 @@ sign.save=Enregistrer le sceau
|
|||||||
sign.personalSigs=Sceaux personnels
|
sign.personalSigs=Sceaux personnels
|
||||||
sign.sharedSigs=Sceaux partagés
|
sign.sharedSigs=Sceaux partagés
|
||||||
sign.noSavedSigs=Aucun sceau enregistré trouvé
|
sign.noSavedSigs=Aucun sceau enregistré trouvé
|
||||||
sign.addToAll=Add to all pages
|
sign.addToAll=Ajouter à toutes les pages
|
||||||
sign.delete=Delete
|
sign.delete=Supprimer
|
||||||
sign.first=First page
|
sign.first=Première page
|
||||||
sign.last=Last page
|
sign.last=Dernière page
|
||||||
sign.next=Next page
|
sign.next=Page suivante
|
||||||
sign.previous=Previous page
|
sign.previous=Page précédente
|
||||||
sign.maintainRatio=Toggle maintain aspect ratio
|
sign.maintainRatio=Conserver les proportions
|
||||||
|
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Convertir
|
|||||||
compress.title=Compresser un PDF
|
compress.title=Compresser un PDF
|
||||||
compress.header=Compresser un PDF (lorsque c'est possible!)
|
compress.header=Compresser un PDF (lorsque c'est possible!)
|
||||||
compress.credit=Ce service utilise qpdf 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.1=Mode manuel – de 1 à 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Niveau d'optimisation
|
compress.selectText.2=Niveau d'optimisation
|
||||||
compress.selectText.3=4 (terrible pour les images textuelles)
|
compress.selectText.3=4 (terrible pour les images textuelles)
|
||||||
compress.selectText.4=Mode automatique – ajuste automatiquement la qualité pour obtenir le PDF à la taille exacte
|
compress.selectText.4=Mode automatique – ajuste automatiquement la qualité pour obtenir le PDF à la taille exacte
|
||||||
@@ -1002,14 +1011,14 @@ multiTool.undo=Undo
|
|||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
#decrypt
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
decrypt.passwordPrompt=Ce fichier est protégé par un mot de passe. Veuillez saisir le mot de passe :
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
decrypt.cancelled=Operation annulée pour le PDF: {0}
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
decrypt.noPassword=Pas de mot de passe fourni pour le PDF chiffré : {0}
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
decrypt.invalidPassword=Veuillez réessayer avec le bon mot de passe
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
decrypt.invalidPasswordHeader=Mauvais mot de passe ou chiffrement non supporté pour le PDF : {0}
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
decrypt.unexpectedError=Une erreur est survenue lors de traitement du fichier. Veuillez essayer de nouveau.
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
decrypt.serverError=Erreur du serveur lors du déchiffrement : {0}
|
||||||
decrypt.success=File decrypted successfully.
|
decrypt.success=Fichier déchiffré avec succès.
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
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 !
|
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 !
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Marcáil síos go PDF
|
|||||||
home.MarkdownToPDF.desc=Tiontaíonn aon chomhad Markdown go PDF
|
home.MarkdownToPDF.desc=Tiontaíonn aon chomhad Markdown go PDF
|
||||||
MarkdownToPDF.tags=marcáil, ábhar gréasáin, claochlú, tiontú
|
MarkdownToPDF.tags=marcáil, ábhar gréasáin, claochlú, tiontú
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Faigh GACH Eolas ar PDF
|
home.getPdfInfo.title=Faigh GACH Eolas ar PDF
|
||||||
home.getPdfInfo.desc=Grab aon fhaisnéis agus is féidir ar PDFs
|
home.getPdfInfo.desc=Grab aon fhaisnéis agus is féidir ar PDFs
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Obair idir lámha
|
|||||||
MarkdownToPDF.credit=Úsáideann WeasyPrint
|
MarkdownToPDF.credit=Úsáideann WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL go PDF
|
URLToPDF.title=URL go PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Tiontaigh go PDF
|
|||||||
compress.title=Comhbhrúigh
|
compress.title=Comhbhrúigh
|
||||||
compress.header=Comhbhrúigh PDF
|
compress.header=Comhbhrúigh PDF
|
||||||
compress.credit=Úsáideann an tseirbhís seo qpdf 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.1=Mód Láimhe - Ó 1 go 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Leibhéal optamaithe:
|
compress.selectText.2=Leibhéal optamaithe:
|
||||||
compress.selectText.3=4 (Uafásach le haghaidh íomhánna téacs)
|
compress.selectText.3=4 (Uafásach le haghaidh íomhánna téacs)
|
||||||
compress.selectText.4=Mód uathoibríoch - Coigeartaíonn Auto cáilíocht chun PDF a fháil go dtí an méid cruinn
|
compress.selectText.4=Mód uathoibríoch - Coigeartaíonn Auto cáilíocht chun PDF a fháil go dtí an méid cruinn
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown u PDF
|
|||||||
home.MarkdownToPDF.desc=Pretvara bilo koju Markdown datoteku u PDF
|
home.MarkdownToPDF.desc=Pretvara bilo koju Markdown datoteku u PDF
|
||||||
MarkdownToPDF.tags=oznake,web-sadržaj,transformacija,konvertiranje
|
MarkdownToPDF.tags=oznake,web-sadržaj,transformacija,konvertiranje
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Dohvati SVE informacije o PDF-u
|
home.getPdfInfo.title=Dohvati SVE informacije o PDF-u
|
||||||
home.getPdfInfo.desc=Dohvaća sve moguće informacije o PDF-ovima
|
home.getPdfInfo.desc=Dohvaća sve moguće informacije o PDF-ovima
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Rad u toku
|
|||||||
MarkdownToPDF.credit=Koristi WeasyPrint
|
MarkdownToPDF.credit=Koristi WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL u PDF
|
URLToPDF.title=URL u PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Pretvori u PDF
|
|||||||
compress.title=Komprimirajte
|
compress.title=Komprimirajte
|
||||||
compress.header=Komprimirajte PDF
|
compress.header=Komprimirajte PDF
|
||||||
compress.credit=Ova usluga koristi qpdf 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.1=Ručni režim - Od 1 do 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Nivo optimizacije:
|
compress.selectText.2=Nivo optimizacije:
|
||||||
compress.selectText.3=4 (Užasno za tekstualne slike)
|
compress.selectText.3=4 (Užasno za tekstualne slike)
|
||||||
compress.selectText.4=Automatski način - Automatski prilagođava kvalitetu kako bi PDF dobio točnu veličinu
|
compress.selectText.4=Automatski način - Automatski prilagođava kvalitetu kako bi PDF dobio točnu veličinu
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Penurunan harga ke PDF
|
|||||||
home.MarkdownToPDF.desc=Mengonversi berkas Markdown apa pun ke PDF
|
home.MarkdownToPDF.desc=Mengonversi berkas Markdown apa pun ke PDF
|
||||||
MarkdownToPDF.tags=markup, konten web, transformasi, konversi
|
MarkdownToPDF.tags=markup, konten web, transformasi, konversi
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Dapatkan Semua Info tentang PDF
|
home.getPdfInfo.title=Dapatkan Semua Info tentang PDF
|
||||||
home.getPdfInfo.desc=Mengambil setiap dan semua informasi yang mungkin ada pada PDF
|
home.getPdfInfo.desc=Mengambil setiap dan semua informasi yang mungkin ada pada PDF
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Pekerjaan sedang berlangsung
|
|||||||
MarkdownToPDF.credit=Menggunakan WeasyPrint
|
MarkdownToPDF.credit=Menggunakan WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL ke PDF
|
URLToPDF.title=URL ke PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Konversi ke PDF
|
|||||||
compress.title=Kompres
|
compress.title=Kompres
|
||||||
compress.header=Kompres PDF
|
compress.header=Kompres PDF
|
||||||
compress.credit=Layanan ini menggunakan qpdf 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.1=Mode Manual - Dari 1 hingga 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Tingkat Optimalisasi:
|
compress.selectText.2=Tingkat Optimalisasi:
|
||||||
compress.selectText.3=4 (Buruk untuk gambar teks)
|
compress.selectText.3=4 (Buruk untuk gambar teks)
|
||||||
compress.selectText.4=Mode Otomatis - Menyesuaikan kualitas secara otomatis untuk mendapatkan PDF dengan ukuran yang tepat
|
compress.selectText.4=Mode Otomatis - Menyesuaikan kualitas secara otomatis untuk mendapatkan PDF dengan ukuran yang tepat
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown in PDF
|
|||||||
home.MarkdownToPDF.desc=Converte qualsiasi file Markdown in PDF
|
home.MarkdownToPDF.desc=Converte qualsiasi file Markdown in PDF
|
||||||
MarkdownToPDF.tags=markup,contenuto web,trasformazione,conversione
|
MarkdownToPDF.tags=markup,contenuto web,trasformazione,conversione
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF in Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converte qualsiasi PDF in Markdown
|
||||||
|
PDFToMarkdown.tags=markup,contenuto-web,trasformazione,convertire,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Ottieni TUTTE le informazioni in PDF
|
home.getPdfInfo.title=Ottieni TUTTE le informazioni in PDF
|
||||||
home.getPdfInfo.desc=Raccogli tutte le informazioni possibili sui PDF
|
home.getPdfInfo.desc=Raccogli tutte le informazioni possibili sui PDF
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Conversione in corso
|
|||||||
MarkdownToPDF.credit=Utilizza WeasyPrint
|
MarkdownToPDF.credit=Utilizza WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF in Markdown
|
||||||
|
PDFToMarkdown.header=PDF in Markdown
|
||||||
|
PDFToMarkdown.submit=Converti
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL a PDF
|
URLToPDF.title=URL a PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Converti in PDF
|
|||||||
compress.title=Comprimi
|
compress.title=Comprimi
|
||||||
compress.header=Comprimi PDF
|
compress.header=Comprimi PDF
|
||||||
compress.credit=Questo servizio utilizza qpdf 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.1=Modalità manuale - Da 1 a 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Livello di ottimizzazione:
|
compress.selectText.2=Livello di ottimizzazione:
|
||||||
compress.selectText.3=4 (Terribile per le immagini di testo)
|
compress.selectText.3=4 (Terribile per le immagini di testo)
|
||||||
compress.selectText.4=Modalità automatica - Regola automaticamente la qualità per ottenere le dimensioni esatte del PDF
|
compress.selectText.4=Modalità automatica - Regola automaticamente la qualità per ottenere le dimensioni esatte del PDF
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=MarkdownをPDFに変換
|
|||||||
home.MarkdownToPDF.desc=あらゆるMarkdownファイルをPDFに変換します。
|
home.MarkdownToPDF.desc=あらゆるMarkdownファイルをPDFに変換します。
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=PDFのすべての情報を入手
|
home.getPdfInfo.title=PDFのすべての情報を入手
|
||||||
home.getPdfInfo.desc=PDFのあらゆる情報を取得します。
|
home.getPdfInfo.desc=PDFのあらゆる情報を取得します。
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=処理中
|
|||||||
MarkdownToPDF.credit=WeasyPrintを使用
|
MarkdownToPDF.credit=WeasyPrintを使用
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URLをPDFに変換
|
URLToPDF.title=URLをPDFに変換
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=PDFを変換
|
|||||||
compress.title=圧縮
|
compress.title=圧縮
|
||||||
compress.header=PDFを圧縮
|
compress.header=PDFを圧縮
|
||||||
compress.credit=本サービスはPDFの圧縮/最適化にqpdfを使用しています。
|
compress.credit=本サービスはPDFの圧縮/最適化にqpdfを使用しています。
|
||||||
compress.selectText.1=手動モード - 1 から 4
|
compress.selectText.1=手動モード - 1 から 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=品質レベル:
|
compress.selectText.2=品質レベル:
|
||||||
compress.selectText.3=4 (テキスト画像は最悪)
|
compress.selectText.3=4 (テキスト画像は最悪)
|
||||||
compress.selectText.4=自動モード - PDFを正確なサイズにするために品質を自動調整する。
|
compress.selectText.4=自動モード - PDFを正確なサイズにするために品質を自動調整する。
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown naar PDF
|
|||||||
home.MarkdownToPDF.desc=Zet Markdown-bestand om naar PDF
|
home.MarkdownToPDF.desc=Zet Markdown-bestand om naar PDF
|
||||||
MarkdownToPDF.tags=markup,web-inhoud,transformatie,omzetten
|
MarkdownToPDF.tags=markup,web-inhoud,transformatie,omzetten
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Haal ALLE informatie op over PDF
|
home.getPdfInfo.title=Haal ALLE informatie op over PDF
|
||||||
home.getPdfInfo.desc=Haalt alle mogelijke informatie op van PDF's
|
home.getPdfInfo.desc=Haalt alle mogelijke informatie op van PDF's
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=In ontwikkeling
|
|||||||
MarkdownToPDF.credit=Gebruikt WeasyPrint
|
MarkdownToPDF.credit=Gebruikt WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL naar PDF
|
URLToPDF.title=URL naar PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Omzetten naar PDF
|
|||||||
compress.title=Comprimeren
|
compress.title=Comprimeren
|
||||||
compress.header=PDF comprimeren
|
compress.header=PDF comprimeren
|
||||||
compress.credit=Deze functie gebruikt qpdf 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.1=Handmatige modus - Van 1 tot 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Optimalisatieniveau:
|
compress.selectText.2=Optimalisatieniveau:
|
||||||
compress.selectText.3=4 (Verschrikkelijk voor tekstafbeeldingen)
|
compress.selectText.3=4 (Verschrikkelijk voor tekstafbeeldingen)
|
||||||
compress.selectText.4=Automatische modus - Past kwaliteit automatisch aan om PDF naar exacte grootte te krijgen
|
compress.selectText.4=Automatische modus - Past kwaliteit automatisch aan om PDF naar exacte grootte te krijgen
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown til PDF
|
|||||||
home.MarkdownToPDF.desc=Konverter hvilken som helst Markdown-fil til PDF
|
home.MarkdownToPDF.desc=Konverter hvilken som helst Markdown-fil til PDF
|
||||||
MarkdownToPDF.tags=markup,web-innhold,transformasjon,konverter
|
MarkdownToPDF.tags=markup,web-innhold,transformasjon,konverter
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Få ALL informasjon om PDF
|
home.getPdfInfo.title=Få ALL informasjon om PDF
|
||||||
home.getPdfInfo.desc=Fanger opp all tilgjengelig informasjon om PDF-er
|
home.getPdfInfo.desc=Fanger opp all tilgjengelig informasjon om PDF-er
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Arbeid pågår
|
|||||||
MarkdownToPDF.credit=Bruker WeasyPrint
|
MarkdownToPDF.credit=Bruker WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL Til PDF
|
URLToPDF.title=URL Til PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Konverter til PDF
|
|||||||
compress.title=Komprimer
|
compress.title=Komprimer
|
||||||
compress.header=Komprimer PDF
|
compress.header=Komprimer PDF
|
||||||
compress.credit=Denne tjenesten bruker qpdf 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.1=Manuell modus - Fra 1 til 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Optimeringsnivå:
|
compress.selectText.2=Optimeringsnivå:
|
||||||
compress.selectText.3=4 (Dårlig for tekstbilder)
|
compress.selectText.3=4 (Dårlig for tekstbilder)
|
||||||
compress.selectText.4=Automatisk modus - Justerer automatisk kvaliteten for å få PDF til nøyaktig størrelse
|
compress.selectText.4=Automatisk modus - Justerer automatisk kvaliteten for å få PDF til nøyaktig størrelse
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown do PDF
|
|||||||
home.MarkdownToPDF.desc=Zapisuje dokument Markdown do PDF
|
home.MarkdownToPDF.desc=Zapisuje dokument Markdown do PDF
|
||||||
MarkdownToPDF.tags=znaczniki, treść internetowa, transformacja, konwertowanie
|
MarkdownToPDF.tags=znaczniki, treść internetowa, transformacja, konwertowanie
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Pobierz informacje o pliku PDF
|
home.getPdfInfo.title=Pobierz informacje o pliku PDF
|
||||||
home.getPdfInfo.desc=Pobiera wszelkie informacje o pliku PDF
|
home.getPdfInfo.desc=Pobiera wszelkie informacje o pliku PDF
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Prace trwają
|
|||||||
MarkdownToPDF.credit=Użyj WeasyPrint
|
MarkdownToPDF.credit=Użyj WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL do PDF
|
URLToPDF.title=URL do PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Konwertuj na PDF
|
|||||||
compress.title=Kompresuj
|
compress.title=Kompresuj
|
||||||
compress.header=Kompresuj PDF
|
compress.header=Kompresuj PDF
|
||||||
compress.credit=Ta usługa używa qpdf 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.1=Tryb ręczny - Od 1 do 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Poziom optymalizacji:
|
compress.selectText.2=Poziom optymalizacji:
|
||||||
compress.selectText.3=4 (Duże dla obrazów tekstowych)
|
compress.selectText.3=4 (Duże dla obrazów tekstowych)
|
||||||
compress.selectText.4=Tryb automatyczny - Automatycznie dostosowuje jakość, aby uzyskać dokładny rozmiar pliku PDF
|
compress.selectText.4=Tryb automatyczny - Automatycznie dostosowuje jakość, aby uzyskać dokładny rozmiar pliku PDF
|
||||||
|
|||||||
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown para PDF
|
|||||||
home.MarkdownToPDF.desc=Converte qualquer arquivo Markdown para PDF.
|
home.MarkdownToPDF.desc=Converte qualquer arquivo Markdown para PDF.
|
||||||
MarkdownToPDF.tags=marcação,conteúdo-web,transformação,converter
|
MarkdownToPDF.tags=marcação,conteúdo-web,transformação,converter
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Obter Informações de um PDF
|
home.getPdfInfo.title=Obter Informações de um PDF
|
||||||
home.getPdfInfo.desc=Obtém informações (metadata) de um PDF.
|
home.getPdfInfo.desc=Obtém informações (metadata) de um PDF.
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Em desenvolvimento.
|
|||||||
MarkdownToPDF.credit=Utiliza o WeasyPrint.
|
MarkdownToPDF.credit=Utiliza o WeasyPrint.
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=Converter URL/Site para PDF
|
URLToPDF.title=Converter URL/Site para PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Converter para PDF
|
|||||||
compress.title=Comprimir
|
compress.title=Comprimir
|
||||||
compress.header=Comprimir
|
compress.header=Comprimir
|
||||||
compress.credit=Este serviço usa o Qpdf 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 - 1 (Menos) a 9 (Mais)
|
compress.selectText.1=Modo Manual - De 1 a 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Nível de Otimização:
|
compress.selectText.2=Nível de Otimização:
|
||||||
compress.selectText.3=4 (Pior para imagens de texto)
|
compress.selectText.3=4 (Pior para imagens de texto)
|
||||||
compress.selectText.4=Modo Automático - Ajusta automaticamente a qualidade para atingir o tamanho exato desejado
|
compress.selectText.4=Modo Automático - Ajusta automaticamente a qualidade para atingir o tamanho exato desejado
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown în PDF
|
|||||||
home.MarkdownToPDF.desc=Convertește orice fișier Markdown în PDF
|
home.MarkdownToPDF.desc=Convertește orice fișier Markdown în PDF
|
||||||
MarkdownToPDF.tags=markup,conținut-web,transformare,convertește
|
MarkdownToPDF.tags=markup,conținut-web,transformare,convertește
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Obține TOATE Informațiile despre PDF
|
home.getPdfInfo.title=Obține TOATE Informațiile despre PDF
|
||||||
home.getPdfInfo.desc=Extrage orice și toate informațiile posibile despre PDF-uri
|
home.getPdfInfo.desc=Extrage orice și toate informațiile posibile despre PDF-uri
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Lucrare în curs
|
|||||||
MarkdownToPDF.credit=Folosește WeasyPrint
|
MarkdownToPDF.credit=Folosește WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL în PDF
|
URLToPDF.title=URL în PDF
|
||||||
@@ -933,11 +941,12 @@ fileToPDF.submit=Convertiți în PDF
|
|||||||
compress.title=Comprimare
|
compress.title=Comprimare
|
||||||
compress.header=Comprimare PDF
|
compress.header=Comprimare PDF
|
||||||
compress.credit=Acest serviciu utilizează qpdf pentru comprimarea/optimizarea PDF-urilor.
|
compress.credit=Acest serviciu utilizează qpdf pentru comprimarea/optimizarea PDF-urilor.
|
||||||
compress.selectText.1=Nivel de optimizare:
|
compress.selectText.1=Modul manual - de la 1 la 5
|
||||||
compress.selectText.2=0 (Fără optimizare)
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.3=1 (Implicit, optimizare fără pierdere)
|
compress.selectText.2=Nivel de optimizare:
|
||||||
compress.selectText.4=2 (Optimizare cu pierdere)
|
compress.selectText.3=4 (Îngrozitor pentru imaginile text)
|
||||||
compress.selectText.5=3 (Optimizare cu pierdere, mai agresivă)
|
compress.selectText.4=Mod automat - ajustează automat calitatea pentru a aduce PDF-ul la dimensiunea exactă
|
||||||
|
compress.selectText.5=Dimensiunea PDF așteptată (de ex. 25MB, 10.8MB, 25KB)
|
||||||
compress.submit=Comprimare
|
compress.submit=Comprimare
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -452,6 +452,9 @@ home.MarkdownToPDF.title=Markdown do PDF
|
|||||||
home.MarkdownToPDF.desc=Konvertuje akýkoľvek Markdown súbor do PDF
|
home.MarkdownToPDF.desc=Konvertuje akýkoľvek Markdown súbor do PDF
|
||||||
MarkdownToPDF.tags=markup, webový obsah, transformácia, konvertovať
|
MarkdownToPDF.tags=markup, webový obsah, transformácia, konvertovať
|
||||||
|
|
||||||
|
home.PDFToMarkdown.title=PDF to Markdown
|
||||||
|
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
||||||
|
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Získať všetky informácie o PDF
|
home.getPdfInfo.title=Získať všetky informácie o PDF
|
||||||
home.getPdfInfo.desc=Získava všetky dostupné informácie o PDF
|
home.getPdfInfo.desc=Získava všetky dostupné informácie o PDF
|
||||||
@@ -646,6 +649,11 @@ MarkdownToPDF.help=Práca prebieha
|
|||||||
MarkdownToPDF.credit=Používa WeasyPrint
|
MarkdownToPDF.credit=Používa WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
#pdf-to-markdown
|
||||||
|
PDFToMarkdown.title=PDF To Markdown
|
||||||
|
PDFToMarkdown.header=PDF To Markdown
|
||||||
|
PDFToMarkdown.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL do PDF
|
URLToPDF.title=URL do PDF
|
||||||
@@ -933,7 +941,8 @@ fileToPDF.submit=Konvertovať do PDF
|
|||||||
compress.title=Komprimovať
|
compress.title=Komprimovať
|
||||||
compress.header=Komprimovať PDF
|
compress.header=Komprimovať PDF
|
||||||
compress.credit=Táto služba používa qpdf 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.1=Manuálny režim - Od 1 do 5
|
||||||
|
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
|
||||||
compress.selectText.2=Úroveň optimalizácie:
|
compress.selectText.2=Úroveň optimalizácie:
|
||||||
compress.selectText.3=4 (Hrozné pre textové obrázky)
|
compress.selectText.3=4 (Hrozné pre textové obrázky)
|
||||||
compress.selectText.4=Automatický režim - Automaticky upravuje kvalitu, aby sa PDF dostalo na presnú veľkosť
|
compress.selectText.4=Automatický režim - Automaticky upravuje kvalitu, aby sa PDF dostalo na presnú veľkosť
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user