diff --git a/.gitignore b/.gitignore
index ba9d755d98d..c5a8f2e7693 100644
--- a/.gitignore
+++ b/.gitignore
@@ -110,7 +110,6 @@ watchedFolders/
*.war
*.nar
*.ear
-*.zip
*.tar.gz
*.rar
*.db
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dad8928ded3..47f6215cbd4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -29,7 +29,7 @@ If you would like to add or modify a translation, please see [How to add new lan
## Docs
-Documentation for Stirling-PDF is handled in a separate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use "edit this page"-button at the bottom of each page at [https://stirlingtools.com/docs/](https://stirlingtools.com/docs/).
+Documentation for Stirling-PDF is handled in a separate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use "edit this page"-button at the bottom of each page at [https://docs.stirlingpdf.com/](https://docs.stirlingpdf.com/).
## Fixing Bugs or Adding a New Feature
@@ -41,4 +41,4 @@ If, at any point of time, you have a question, please feel free to ask in the sa
## License
-By contributing to this project, you agree that your contributions will be licensed under the [GPL 3 License](LICENSE). You also acknowledge and agree that your contributions will be included in Stirling-PDF and that they can be relicensed in the future under the MPL 2.0 (Mozilla Public License Version 2.0) license.
+By contributing to this project, you agree that your contributions will be licensed under the [MIT License](LICENSE).
diff --git a/DeveloperGuide.md b/DeveloperGuide.md
new file mode 100644
index 00000000000..eaed2afb35d
--- /dev/null
+++ b/DeveloperGuide.md
@@ -0,0 +1,557 @@
+# Stirling-PDF Developer Guide
+
+## 1. Introduction
+
+Stirling-PDF is a robust, locally hosted web-based PDF manipulation tool. This guide focuses on Docker-based development and testing, which is the recommended approach for working with the full version of Stirling-PDF.
+
+## 2. Project Overview
+
+Stirling-PDF is built using:
+- Spring Boot + Thymeleaf
+- PDFBox
+- LibreOffice
+- OcrMyPdf
+- HTML, CSS, JavaScript
+- Docker
+- PDF.js
+- PDF-LIB.js
+- Lombok
+
+## 3. Development Environment Setup
+
+### Prerequisites
+- Docker
+- Git
+- Java JDK 17 or later
+- Gradle 7.0 or later (Included within repo)
+
+### Setup Steps
+1. Clone the repository:
+ ```
+ git clone https://github.com/Stirling-Tools/Stirling-PDF.git
+ cd Stirling-PDF
+ ```
+
+2. Install Docker and JDK17 if not already installed.
+
+3. Install a recommended Java IDE such as Eclipse, IntelliJ or VSCode
+
+4. Lombok Setup
+Stirling-PDF uses Lombok to reduce boilerplate code. Some IDEs, like Eclipse, don't support Lombok out of the box. To set up Lombok in your development environment:
+Visit the [Lombok website](https://projectlombok.org/setup/) for installation instructions specific to your IDE.
+
+5. Add environment variable
+For local testing you should generally be testing the full 'Security' version of Stirling-PDF to do this you must add the environment flag DOCKER_ENABLE_SECURITY=true to your system and/or IDE build/run step
+
+
+## 4. Project Structure
+
+```
+Stirling-PDF/
+├── .github/ # GitHub-specific files (workflows, issue templates)
+├── configs/ # Configuration files used by stirling at runtime (generated at runtime)
+├── cucumber/ # Cucumber test files
+│ ├── features/
+├── customFiles/ # Custom static files and templates (generated at runtime used to replace existing files)
+├── docs/ # Documentation files
+├── exampleYmlFiles/ # Example YAML configuration files
+├── images/ # Image assets
+├── pipeline/ # Pipeline-related files (generated at runtime)
+├── scripts/ # Utility scripts
+├── src/ # Source code
+│ ├── main/
+│ │ ├── java/
+│ │ │ └── stirling/
+│ │ │ └── software/
+│ │ │ └── SPDF/
+│ │ │ ├── config/
+│ │ │ ├── controller/
+│ │ │ ├── model/
+│ │ │ ├── repository/
+│ │ │ ├── service/
+│ │ │ └── utils/
+│ │ └── resources/
+│ │ ├── static/
+│ │ │ ├── css/
+│ │ │ ├── js/
+│ │ │ └── pdfjs/
+│ │ └── templates/
+│ └── test/
+│ └── java/
+│ └── stirling/
+│ └── software/
+│ └── SPDF/
+├── build.gradle # Gradle build configuration
+├── Dockerfile # Main Dockerfile
+├── Dockerfile-ultra-lite # Dockerfile for ultra-lite version
+├── Dockerfile-fat # Dockerfile for fat version
+├── docker-compose.yml # Docker Compose configuration
+└── test.sh # Test script to deploy all docker versions and run cuke tests
+```
+
+## 5. Docker-based Development
+
+Stirling-PDF offers several Docker versions:
+- Full: All features included
+- Ultra-Lite: Basic PDF operations only
+- Fat: Includes additional libraries and fonts predownloaded
+
+### Example Docker Compose Files
+
+Stirling-PDF provides several example Docker Compose files in the `exampleYmlFiles` directory such as :
+
+- `docker-compose-latest.yml`: Latest version without security features
+- `docker-compose-latest-security.yml`: Latest version with security features enabled
+- `docker-compose-latest-fat-security.yml`: Fat version with security features enabled
+
+These files provide pre-configured setups for different scenarios. For example, here's a snippet from `docker-compose-latest-security.yml`:
+
+```yaml
+services:
+ stirling-pdf:
+ container_name: Stirling-PDF-Security
+ image: frooodle/s-pdf:latest
+ deploy:
+ resources:
+ limits:
+ memory: 4G
+ healthcheck:
+ test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"]
+ interval: 5s
+ timeout: 10s
+ retries: 16
+ ports:
+ - "8080:8080"
+ volumes:
+ - /stirling/latest/data:/usr/share/tessdata:rw
+ - /stirling/latest/config:/configs:rw
+ - /stirling/latest/logs:/logs:rw
+ environment:
+ DOCKER_ENABLE_SECURITY: "true"
+ SECURITY_ENABLELOGIN: "true"
+ PUID: 1002
+ PGID: 1002
+ UMASK: "022"
+ SYSTEM_DEFAULTLOCALE: en-US
+ UI_APPNAME: Stirling-PDF
+ UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security
+ UI_APPNAMENAVBAR: Stirling-PDF Latest
+ SYSTEM_MAXFILESIZE: "100"
+ METRICS_ENABLED: "true"
+ SYSTEM_GOOGLEVISIBILITY: "true"
+ restart: on-failure:5
+```
+
+To use these example files, copy the desired file to your project root and rename it to `docker-compose.yml`, or specify the file explicitly when running Docker Compose:
+
+```bash
+docker-compose -f exampleYmlFiles/docker-compose-latest-security.yml up
+```
+
+### Building Docker Images
+
+Stirling-PDF uses different Docker images for various configurations. The build process is controlled by environment variables and uses specific Dockerfile variants. Here's how to build the Docker images:
+
+1. Set the security environment variable:
+ ```bash
+ export DOCKER_ENABLE_SECURITY=false # or true for security-enabled builds
+ ```
+
+2. Build the project with Gradle:
+ ```bash
+ ./gradlew clean build
+ ```
+
+3. Build the Docker images:
+
+ For the latest version:
+ ```bash
+ docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest -f ./Dockerfile .
+ ```
+
+ For the ultra-lite version:
+ ```bash
+ docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-ultra-lite -f ./Dockerfile-ultra-lite .
+ ```
+
+ For the fat version (with security enabled):
+ ```bash
+ export DOCKER_ENABLE_SECURITY=true
+ docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-fat -f ./Dockerfile-fat .
+ ```
+
+Note: The `--no-cache` and `--pull` flags ensure that the build process uses the latest base images and doesn't use cached layers, which is useful for testing and ensuring reproducible builds. however to improve build times these can often be removed depending on your usecase
+
+
+
+## 6. Testing
+
+### Comprehensive Testing Script
+
+Stirling-PDF provides a `test.sh` script in the root directory. This script builds all versions of Stirling-PDF, checks that each version works, and runs Cucumber tests. It's recommended to run this script before submitting a final pull request.
+
+To run the test script:
+
+```bash
+./test.sh
+```
+
+This script performs the following actions:
+1. Builds all Docker images (full, ultra-lite, fat)
+2. Runs each version to ensure it starts correctly
+3. Executes Cucumber tests against main version and ensures feature compatibility, in the event these tests fail your PR will not be merged
+
+Note: The `test.sh` script will run automatically when you raise a PR. However, it's recommended to run it locally first to save resources and catch any issues early.
+
+### Full Testing with Docker
+
+1. Build and run the Docker container per the above instructions:
+
+2. Access the application at `http://localhost:8080` and manually test all features developed.
+
+
+### Local Testing (Java and UI Components)
+
+For quick iterations and development of Java backend, JavaScript, and UI components, you can run and test Stirling-PDF locally without Docker. This approach allows you to work on and verify changes to:
+
+- Java backend logic
+- RESTful API endpoints
+- JavaScript functionality
+- User interface components and styling
+- Thymeleaf templates
+
+To run Stirling-PDF locally:
+
+1. Compile and run the project using built in IDE methods or by running:
+ ```
+ ./gradlew bootRun
+ ```
+
+2. Access the application at `http://localhost:8080` in your web browser.
+
+3. Manually test the features you're working on through the UI.
+
+4. For API changes, use tools like Postman or curl to test endpoints directly.
+
+Important notes:
+- Local testing doesn't include features that depend on external tools like OCRmyPDF, LibreOffice, or Python scripts.
+- There are currently no automated unit tests. All testing is done manually through the UI or API calls. (You are welcome to add JUnits!)
+- Always verify your changes in the full Docker environment before submitting pull requests, as some integrations and features will only work in the complete setup.
+
+
+## 7. Contributing
+
+1. Fork the repository on GitHub.
+2. Create a new branch for your feature or bug fix.
+3. Make your changes and commit them with clear, descriptive messages and ensure any documentation is updated related to your changes.
+4. Test your changes thoroughly in the Docker environment.
+5. Run the `test.sh` script to ensure all versions build correctly and pass the Cucumber tests:
+ ```bash
+ ./test.sh
+ ```
+6. Push your changes to your fork.
+7. Submit a pull request to the main repository.
+8. See additional [contributing guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
+
+When you raise a PR:
+- The `test.sh` script will run automatically against your PR.
+- The PR checks will verify versioning and dependency updates.
+- Documentation will be automatically updated for dependency changes.
+- Security issues will be checked using Snyk and PixeeBot.
+
+Address any issues that arise from these checks before finalizing your pull request.
+
+## 8. API Documentation
+
+API documentation is available at `/swagger-ui/index.html` when running the application. You can also view the latest API documentation [here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/).
+
+## 9. Customization
+
+Stirling-PDF can be customized through environment variables or a `settings.yml` file. Key customization options include:
+- Application name and branding
+- Security settings
+- UI customization
+- Endpoint management
+
+When using Docker, pass environment variables using the `-e` flag or in your `docker-compose.yml` file.
+
+Example:
+```
+docker run -p 8080:8080 -e APP_NAME="My PDF Tool" stirling-pdf:full
+```
+
+Refer to the main README for a full list of customization options.
+
+## 10. Language Translations
+
+For managing language translations that affect multiple files, Stirling-PDF provides a helper script:
+
+```bash
+/scripts/replace_translation_line.sh
+```
+
+This script helps you make consistent replacements across language files.
+
+When contributing translations:
+1. Use the helper script for multi-file changes.
+2. Ensure all language files are updated consistently.
+3. The PR checks will verify consistency in language file updates.
+
+Remember to test your changes thoroughly to ensure they don't break any existing functionality.
+
+
+
+
+# Code examples
+
+### Overview of Thymeleaf
+
+Thymeleaf is a server-side Java HTML template engine. It is used in Stirling-PDF to render dynamic web pages. Thymeleaf integrates heavily with Spring Boot
+
+### Thymeleaf overview
+
+In Stirling-PDF, Thymeleaf is used to create HTML templates that are rendered on the server side. These templates are located in the `src/main/resources/templates` directory. Thymeleaf templates use a combination of HTML and special Thymeleaf attributes to dynamically generate content.
+Some examples of this are
+```html
+
+or
+
+```
+Where it uses the th:block, th: indicating its a special thymeleaf element to be used serverside in generating the html, and block being the actual element type.
+In this case we are inserting the ``navbar`` entry within the ``fragments/navbar.html`` fragment into the ``th:block`` element.
+
+They can be more complex such as
+```html
+
+```
+Which is the same as above but passes the parameters title and header into the fragment common.html to be used in its HTML generation
+
+Thymeleaf can also be used to loop through objects or pass things from java side into html side.
+```java
+ @GetMapping
+ public String newFeaturePage(Model model) {
+ model.addAttribute("exampleData", exampleData);
+ return "new-feature";
+ }
+```
+in above example if exampleData is a list of plain java objects of class Person and within it you had id, name, age etc. You can reference it like so
+```html
+
+
+
+
+
+
+
+
+
+```
+This would generate n entries of tr for each person in exampleData
+
+### Adding a New Feature to the Backend (API)
+
+1. **Create a New Controller:**
+ - Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/api` directory.
+ - Annotate the class with `@RestController` and `@RequestMapping` to define the API endpoint.
+ - Ensure to add API documentation annotations like `@Tag(name = "General", description = "General APIs")` and `@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")`.
+
+ ```java
+ package stirling.software.SPDF.controller.api;
+
+ import org.springframework.web.bind.annotation.GetMapping;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RestController;
+ import io.swagger.v3.oas.annotations.Operation;
+ import io.swagger.v3.oas.annotations.tags.Tag;
+
+ @RestController
+ @RequestMapping("/api/v1/new-feature")
+ @Tag(name = "General", description = "General APIs")
+ public class NewFeatureController {
+
+ @GetMapping
+ @Operation(summary = "New Feature", description = "This is a new feature endpoint.")
+ public String newFeature() {
+ return "NewFeatureResponse"; // This refers to the NewFeatureResponse.html template presenting the user with the generated html from that file when they navigate to /api/v1/new-feature
+ }
+ }
+ ```
+
+2. **Define the Service Layer:** (Not required but often useful)
+ - Create a new service class in the `src/main/java/stirling/software/SPDF/service` directory.
+ - Implement the business logic for the new feature.
+
+ ```java
+ package stirling.software.SPDF.service;
+
+ import org.springframework.stereotype.Service;
+
+ @Service
+ public class NewFeatureService {
+
+ public String getNewFeatureData() {
+ // Implement business logic here
+ return "New Feature Data";
+ }
+ }
+ ```
+
+2b. **Integrate the Service with the Controller:**
+ - Autowire the service class in the controller and use it to handle the API request.
+
+ ```java
+ package stirling.software.SPDF.controller.api;
+
+ import org.springframework.beans.factory.annotation.Autowired;
+ import org.springframework.web.bind.annotation.GetMapping;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RestController;
+ import stirling.software.SPDF.service.NewFeatureService;
+ import io.swagger.v3.oas.annotations.Operation;
+ import io.swagger.v3.oas.annotations.tags.Tag;
+
+ @RestController
+ @RequestMapping("/api/v1/new-feature")
+ @Tag(name = "General", description = "General APIs")
+ public class NewFeatureController {
+
+ @Autowired
+ private NewFeatureService newFeatureService;
+
+ @GetMapping
+ @Operation(summary = "New Feature", description = "This is a new feature endpoint.")
+ public String newFeature() {
+ return newFeatureService.getNewFeatureData();
+ }
+ }
+ ```
+
+### Adding a New Feature to the Frontend (UI)
+
+1. **Create a New Thymeleaf Template:**
+ - Create a new HTML file in the `src/main/resources/templates` directory.
+ - Use Thymeleaf attributes to dynamically generate content.
+ - Use `extract-page.html` as a base example for the HTML template, useful to ensure importing of the general layout, navbar and footer.
+
+ ```html
+
+
+
+
+
+
+
+
+
+
+ ```
+
+2. **Create a New Controller for the UI:**
+ - Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/ui` directory.
+ - Annotate the class with `@Controller` and `@RequestMapping` to define the UI endpoint.
+
+ ```java
+ package stirling.software.SPDF.controller.ui;
+
+ import org.springframework.beans.factory.annotation.Autowired;
+ import org.springframework.stereotype.Controller;
+ import org.springframework.ui.Model;
+ import org.springframework.web.bind.annotation.GetMapping;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import stirling.software.SPDF.service.NewFeatureService;
+
+ @Controller
+ @RequestMapping("/new-feature")
+ public class NewFeatureUIController {
+
+ @Autowired
+ private NewFeatureService newFeatureService;
+
+ @GetMapping
+ public String newFeaturePage(Model model) {
+ model.addAttribute("newFeatureData", newFeatureService.getNewFeatureData());
+ return "new-feature";
+ }
+ }
+ ```
+
+3. **Update the Navigation Bar:**
+ - Add a link to the new feature page in the navigation bar.
+ - Update the `src/main/resources/templates/fragments/navbar.html` file.
+
+ ```html
+
+ New Feature
+
+ ```
+
+
+## Adding New Translations to Existing Language Files in Stirling-PDF
+
+When adding a new feature or modifying existing ones in Stirling-PDF, you'll need to add new translation entries to the existing language files. Here's a step-by-step guide:
+
+### 1. Locate Existing Language Files
+
+Find the existing `messages.properties` files in the `src/main/resources` directory. You'll see files like:
+
+- `messages.properties` (default, usually English)
+- `messages_en_GB.properties`
+- `messages_fr.properties`
+- `messages_de.properties`
+- etc.
+
+### 2. Add New Translation Entries
+
+Open each of these files and add your new translation entries. For example, if you're adding a new feature called "PDF Splitter",
+Use descriptive, hierarchical keys (e.g., `feature.element.description`)
+you might add:
+
+```properties
+pdfSplitter.title=PDF Splitter
+pdfSplitter.description=Split your PDF into multiple documents
+pdfSplitter.button.split=Split PDF
+pdfSplitter.input.pages=Enter page numbers to split
+```
+
+Add these entries to the default GB language file and any others you wish, translating the values as appropriate for each language.
+
+### 3. Use Translations in Thymeleaf Templates
+
+In your Thymeleaf templates, use the `#{key}` syntax to reference the new translations:
+
+```html
+PDF Splitter
+Split your PDF into multiple documents
+
+Split PDF
+```
+
+
+
+Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization.
diff --git a/README.md b/README.md
index 56cb25afc40..95fed569da4 100644
--- a/README.md
+++ b/README.md
@@ -172,42 +172,42 @@ Stirling PDF currently supports 38!
| Language | Progress |
| ------------------------------------------- | -------------------------------------- |
-| Arabic (العربية) (ar_AR) | ![97%](https://geps.dev/progress/97) |
-| Basque (Euskara) (eu_ES) | ![58%](https://geps.dev/progress/58) |
-| Bulgarian (Български) (bg_BG) | ![89%](https://geps.dev/progress/89) |
-| Catalan (Català) (ca_CA) | ![46%](https://geps.dev/progress/46) |
-| Croatian (Hrvatski) (hr_HR) | ![89%](https://geps.dev/progress/89) |
-| Czech (Česky) (cs_CZ) | ![85%](https://geps.dev/progress/85) |
-| Danish (Dansk) (da_DK) | ![94%](https://geps.dev/progress/94) |
-| Dutch (Nederlands) (nl_NL) | ![91%](https://geps.dev/progress/91) |
+| Arabic (العربية) (ar_AR) | ![94%](https://geps.dev/progress/94) |
+| Basque (Euskara) (eu_ES) | ![57%](https://geps.dev/progress/57) |
+| Bulgarian (Български) (bg_BG) | ![99%](https://geps.dev/progress/99) |
+| Catalan (Català) (ca_CA) | ![44%](https://geps.dev/progress/44) |
+| Croatian (Hrvatski) (hr_HR) | ![87%](https://geps.dev/progress/87) |
+| Czech (Česky) (cs_CZ) | ![83%](https://geps.dev/progress/83) |
+| Danish (Dansk) (da_DK) | ![91%](https://geps.dev/progress/91) |
+| Dutch (Nederlands) (nl_NL) | ![88%](https://geps.dev/progress/88) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
-| French (Français) (fr_FR) | ![88%](https://geps.dev/progress/88) |
-| German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
-| Greek (Ελληνικά) (el_GR) | ![78%](https://geps.dev/progress/78) |
-| Hindi (हिंदी) (hi_IN) | ![74%](https://geps.dev/progress/74) |
-| Hungarian (Magyar) (hu_HU) | ![71%](https://geps.dev/progress/71) |
-| Indonesia (Bahasa Indonesia) (id_ID) | ![72%](https://geps.dev/progress/72) |
-| Irish (Gaeilge) (ga_IE) | ![93%](https://geps.dev/progress/93) |
+| French (Français) (fr_FR) | ![85%](https://geps.dev/progress/85) |
+| German (Deutsch) (de_DE) | ![94%](https://geps.dev/progress/94) |
+| Greek (Ελληνικά) (el_GR) | ![75%](https://geps.dev/progress/75) |
+| Hindi (हिंदी) (hi_IN) | ![72%](https://geps.dev/progress/72) |
+| Hungarian (Magyar) (hu_HU) | ![69%](https://geps.dev/progress/69) |
+| Indonesia (Bahasa Indonesia) (id_ID) | ![70%](https://geps.dev/progress/70) |
+| Irish (Gaeilge) (ga_IE) | ![90%](https://geps.dev/progress/90) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
-| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) |
-| Korean (한국어) (ko_KR) | ![80%](https://geps.dev/progress/80) |
-| Norwegian (Norsk) (no_NB) | ![93%](https://geps.dev/progress/93) |
-| Polish (Polski) (pl_PL) | ![87%](https://geps.dev/progress/87) |
-| Portuguese (Português) (pt_PT) | ![74%](https://geps.dev/progress/74) |
-| Portuguese Brazilian (Português) (pt_BR) | ![97%](https://geps.dev/progress/97) |
-| Romanian (Română) (ro_RO) | ![95%](https://geps.dev/progress/95) |
-| Russian (Русский) (ru_RU) | ![79%](https://geps.dev/progress/79) |
-| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![74%](https://geps.dev/progress/74) |
-| Simplified Chinese (简体中文) (zh_CN) | ![96%](https://geps.dev/progress/96) |
-| Slovakian (Slovensky) (sk_SK) | ![87%](https://geps.dev/progress/87) |
-| Spanish (Español) (es_ES) | ![96%](https://geps.dev/progress/96) |
-| Swedish (Svenska) (sv_SE) | ![95%](https://geps.dev/progress/95) |
-| Thai (ไทย) (th_TH) | ![94%](https://geps.dev/progress/94) |
-| Traditional Chinese (繁體中文) (zh_TW) | ![93%](https://geps.dev/progress/93) |
-| Turkish (Türkçe) (tr_TR) | ![97%](https://geps.dev/progress/97) |
-| Ukrainian (Українська) (uk_UA) | ![85%](https://geps.dev/progress/85) |
-| Vietnamese (Tiếng Việt) (vi_VN) | ![94%](https://geps.dev/progress/94) |
+| Japanese (日本語) (ja_JP) | ![87%](https://geps.dev/progress/87) |
+| Korean (한국어) (ko_KR) | ![77%](https://geps.dev/progress/77) |
+| Norwegian (Norsk) (no_NB) | ![90%](https://geps.dev/progress/90) |
+| Polish (Polski) (pl_PL) | ![99%](https://geps.dev/progress/99) |
+| Portuguese (Português) (pt_PT) | ![72%](https://geps.dev/progress/72) |
+| Portuguese Brazilian (Português) (pt_BR) | ![99%](https://geps.dev/progress/99) |
+| Romanian (Română) (ro_RO) | ![92%](https://geps.dev/progress/92) |
+| Russian (Русский) (ru_RU) | ![77%](https://geps.dev/progress/77) |
+| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![72%](https://geps.dev/progress/72) |
+| Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) |
+| Slovakian (Slovensky) (sk_SK) | ![84%](https://geps.dev/progress/84) |
+| Spanish (Español) (es_ES) | ![93%](https://geps.dev/progress/93) |
+| Swedish (Svenska) (sv_SE) | ![92%](https://geps.dev/progress/92) |
+| Thai (ไทย) (th_TH) | ![91%](https://geps.dev/progress/91) |
+| Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) |
+| Turkish (Türkçe) (tr_TR) | ![94%](https://geps.dev/progress/94) |
+| Ukrainian (Українська) (uk_UA) | ![82%](https://geps.dev/progress/82) |
+| Vietnamese (Tiếng Việt) (vi_VN) | ![91%](https://geps.dev/progress/91) |
## Contributing (creating issues, translations, fixing bugs, etc.)
diff --git a/build.gradle b/build.gradle
index 860a574a133..32e2219800f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,13 +16,13 @@ ext {
springBootVersion = "3.3.4"
pdfboxVersion = "3.0.3"
logbackVersion = "1.5.7"
- imageioVersion = "3.11.0"
+ imageioVersion = "3.12.0"
lombokVersion = "1.18.34"
bouncycastleVersion = "1.78.1"
}
group = "stirling.software"
-version = "0.29.0"
+version = "0.30.0"
java {
// 17 is lowest but we support and recommend 21
@@ -32,6 +32,10 @@ java {
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
+ maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" }
+ maven {
+ url 'https://build.shibboleth.net/maven/releases'
+ }
}
licenseReport {
@@ -115,7 +119,7 @@ configurations.all {
}
dependencies {
//security updates
- implementation "org.springframework:spring-webmvc:6.1.13"
+ implementation "org.springframework:spring-webmvc:6.1.14"
implementation("io.github.pixee:java-security-toolkit:1.2.0")
@@ -127,6 +131,9 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion"
+ implementation 'com.posthog.java:posthog:1.1.1'
+ implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1'
+
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
@@ -134,15 +141,25 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
+ implementation 'org.springframework.security:spring-security-saml2-service-provider:6.3.3'
+ implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
//2.2.x requires rebuild of DB file.. need migration path
runtimeOnly "com.h2database:h2:2.1.214"
// implementation "com.h2database:h2:2.2.224"
+ constraints {
+ implementation "org.opensaml:opensaml-core"
+ implementation "org.opensaml:opensaml-saml-api"
+ implementation "org.opensaml:opensaml-saml-impl"
+ }
+ implementation "org.springframework.security:spring-security-saml2-service-provider"
+
+ implementation 'com.coveo:saml-client:5.0.0'
}
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
// Batik
- implementation "org.apache.xmlgraphics:batik-all:1.17"
+ implementation "org.apache.xmlgraphics:batik-all:1.18"
// TwelveMonkeys
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
@@ -184,11 +201,11 @@ dependencies {
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
- implementation "io.micrometer:micrometer-core:1.13.4"
+ implementation "io.micrometer:micrometer-core:1.13.6"
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark
- implementation "org.commonmark:commonmark:0.23.0"
- implementation "org.commonmark:commonmark-ext-gfm-tables:0.23.0"
+ implementation "org.commonmark:commonmark:0.24.0"
+ implementation "org.commonmark:commonmark-ext-gfm-tables:0.24.0"
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
implementation "com.fathzer:javaluator:3.0.5"
diff --git a/chart/stirling-pdf/Chart.yaml b/chart/stirling-pdf/Chart.yaml
index b276a0a2111..4f13c8ef7ff 100644
--- a/chart/stirling-pdf/Chart.yaml
+++ b/chart/stirling-pdf/Chart.yaml
@@ -1,5 +1,5 @@
apiVersion: v2
-appVersion: 0.29.0
+appVersion: 0.30.0
description: locally hosted web application that allows you to perform various operations
on PDF files
home: https://github.com/Stirling-Tools/Stirling-PDF
diff --git a/cucumber/exampleFiles/example.html b/cucumber/exampleFiles/example.html
new file mode 100644
index 00000000000..82e96100735
--- /dev/null
+++ b/cucumber/exampleFiles/example.html
@@ -0,0 +1,11 @@
+
+
+
+
+My First Heading
+
+My first paragraph.
+
+
+
+
diff --git a/cucumber/exampleFiles/example.md b/cucumber/exampleFiles/example.md
new file mode 100644
index 00000000000..10bb117bad8
--- /dev/null
+++ b/cucumber/exampleFiles/example.md
@@ -0,0 +1,16 @@
+header
+============
+
+Header2
+------------
+text
+
+text2
+
+## **PDF Features**
+
+### **Page Operations**
+
+- View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts)
+- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
+- Merge multiple PDFs together into a single resultant file.
\ No newline at end of file
diff --git a/cucumber/exampleFiles/example_html.zip b/cucumber/exampleFiles/example_html.zip
new file mode 100644
index 00000000000..23bd1950c44
Binary files /dev/null and b/cucumber/exampleFiles/example_html.zip differ
diff --git a/cucumber/features/external.feature b/cucumber/features/external.feature
index c1330f523c3..58c0a859c46 100644
--- a/cucumber/features/external.feature
+++ b/cucumber/features/external.feature
@@ -123,7 +123,7 @@ Feature: API Validation
| odt | .odt |
| doc | .doc |
- @ocr
+ @ocr @pdfa1
Scenario: PDFA
Given I use an example file at "exampleFiles/pdfa2.pdf" as parameter "fileInput"
And the request data includes
@@ -134,7 +134,7 @@ Feature: API Validation
And the response file should have extension ".pdf"
And the response file should have size greater than 100
- @ocr
+ @ocr @pdfa2
Scenario: PDFA1
Given I use an example file at "exampleFiles/pdfa1.pdf" as parameter "fileInput"
And the request data includes
@@ -218,6 +218,28 @@ Feature: API Validation
| .odt |
| .pptx |
| .rtf |
-
-
+ @calibre @positive @htmltopdf
+ Scenario: Convert HTML to PDF
+ Given I use an example file at "exampleFiles/example.html" as parameter "fileInput"
+ When I send the API request to the endpoint "/api/v1/convert/html/pdf"
+ Then the response status code should be 200
+ And the response file should have size greater than 100
+ And the response file should have extension ".pdf"
+
+ @calibre @positive @zippedhtmltopdf
+ Scenario: Convert zipped HTML to PDF
+ Given I use an example file at "exampleFiles/example_html.zip" as parameter "fileInput"
+ When I send the API request to the endpoint "/api/v1/convert/html/pdf"
+ Then the response status code should be 200
+ And the response file should have size greater than 100
+ And the response file should have extension ".pdf"
+
+ @calibre @positive @markdowntopdf
+ Scenario: Convert Markdown to PDF
+ Given I use an example file at "exampleFiles/example.md" as parameter "fileInput"
+ When I send the API request to the endpoint "/api/v1/convert/markdown/pdf"
+ Then the response status code should be 200
+ And the response file should have size greater than 100
+ And the response file should have extension ".pdf"
+
\ No newline at end of file
diff --git a/exampleYmlFiles/docker-compose-latest-fat-security.yml b/exampleYmlFiles/docker-compose-latest-fat-security.yml
index f29a8a9fa18..44d5ebef4a1 100644
--- a/exampleYmlFiles/docker-compose-latest-fat-security.yml
+++ b/exampleYmlFiles/docker-compose-latest-fat-security.yml
@@ -7,7 +7,7 @@ services:
limits:
memory: 4G
healthcheck:
- test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"]
+ test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
interval: 5s
timeout: 10s
retries: 16
@@ -19,7 +19,7 @@ services:
- /stirling/latest/logs:/logs:rw
environment:
DOCKER_ENABLE_SECURITY: "true"
- SECURITY_ENABLELOGIN: "true"
+ SECURITY_ENABLELOGIN: "false"
PUID: 1002
PGID: 1002
UMASK: "022"
diff --git a/scripts/remove_translation_keys.sh b/scripts/remove_translation_keys.sh
new file mode 100644
index 00000000000..8a16f281f23
--- /dev/null
+++ b/scripts/remove_translation_keys.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Check if a key was provided
+if [ $# -eq 0 ]; then
+ echo "Please provide a key to remove."
+ exit 1
+fi
+
+key_to_remove="$1"
+
+for file in ../src/main/resources/messages_*.properties; do
+ # If the key ends with a dot, remove all keys starting with it
+ if [[ "$key_to_remove" == *. ]]; then
+ sed -i "/^${key_to_remove//./\\.}/d" "$file"
+ else
+ # Otherwise, remove only the exact key match
+ sed -i "/^${key_to_remove//./\\.}=/d" "$file"
+ fi
+ echo "Updated $file"
+done
\ No newline at end of file
diff --git a/src/main/java/stirling/software/SPDF/EE/EEAppConfig.java b/src/main/java/stirling/software/SPDF/EE/EEAppConfig.java
index 0818aa7df7b..5b1b40702d2 100644
--- a/src/main/java/stirling/software/SPDF/EE/EEAppConfig.java
+++ b/src/main/java/stirling/software/SPDF/EE/EEAppConfig.java
@@ -17,9 +17,10 @@ public class EEAppConfig {
@Autowired ApplicationProperties applicationProperties;
- @Bean(name = "RunningEE")
+ @Autowired private LicenseKeyChecker licenseKeyChecker;
+
+ @Bean(name = "runningEE")
public boolean runningEnterpriseEdition() {
- // TODO: Implement EE detection
- return false;
+ return licenseKeyChecker.getEnterpriseEnabledResult();
}
}
diff --git a/src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java b/src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java
new file mode 100644
index 00000000000..e9b14ac4444
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java
@@ -0,0 +1,204 @@
+package stirling.software.SPDF.EE;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.posthog.java.shaded.org.json.JSONObject;
+
+import lombok.extern.slf4j.Slf4j;
+import stirling.software.SPDF.model.ApplicationProperties;
+import stirling.software.SPDF.utils.GeneralUtils;
+
+@Service
+@Slf4j
+public class KeygenLicenseVerifier {
+ private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
+ private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ private final ApplicationProperties applicationProperties;
+
+ @Autowired
+ public KeygenLicenseVerifier(ApplicationProperties applicationProperties) {
+ this.applicationProperties = applicationProperties;
+ }
+
+ public boolean verifyLicense(String licenseKey) {
+ try {
+ log.info("Checking license key");
+ String machineFingerprint = generateMachineFingerprint();
+
+ // First, try to validate the license
+ JsonNode validationResponse = validateLicense(licenseKey, machineFingerprint);
+ if (validationResponse != null) {
+ boolean isValid = validationResponse.path("meta").path("valid").asBoolean();
+ String licenseId = validationResponse.path("data").path("id").asText();
+ if (!isValid) {
+ String code = validationResponse.path("meta").path("code").asText();
+ log.debug(code);
+ if ("NO_MACHINE".equals(code)
+ || "NO_MACHINES".equals(code)
+ || "FINGERPRINT_SCOPE_MISMATCH".equals(code)) {
+ log.info(
+ "License not activated for this machine. Attempting to activate...");
+ boolean activated =
+ activateMachine(licenseKey, licenseId, machineFingerprint);
+ if (activated) {
+ // Revalidate after activation
+ validationResponse = validateLicense(licenseKey, machineFingerprint);
+ isValid =
+ validationResponse != null
+ && validationResponse
+ .path("meta")
+ .path("valid")
+ .asBoolean();
+ }
+ }
+ }
+ return isValid;
+ }
+
+ return false;
+ } catch (Exception e) {
+ log.error("Error verifying license: " + e.getMessage());
+ return false;
+ }
+ }
+
+ private JsonNode validateLicense(String licenseKey, String machineFingerprint)
+ throws Exception {
+ HttpClient client = HttpClient.newHttpClient();
+ String requestBody =
+ String.format(
+ "{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\"}}}",
+ licenseKey, machineFingerprint);
+ HttpRequest request =
+ HttpRequest.newBuilder()
+ .uri(
+ URI.create(
+ BASE_URL
+ + "/"
+ + ACCOUNT_ID
+ + "/licenses/actions/validate-key"))
+ .header("Content-Type", "application/vnd.api+json")
+ .header("Accept", "application/vnd.api+json")
+ // .header("Authorization", "License " + licenseKey)
+ .POST(HttpRequest.BodyPublishers.ofString(requestBody))
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ log.info(" validateLicenseResponse body: " + response.body());
+ JsonNode jsonResponse = objectMapper.readTree(response.body());
+ if (response.statusCode() == 200) {
+
+ JsonNode metaNode = jsonResponse.path("meta");
+ boolean isValid = metaNode.path("valid").asBoolean();
+
+ String detail = metaNode.path("detail").asText();
+ String code = metaNode.path("code").asText();
+
+ log.debug("License validity: " + isValid);
+ log.debug("Validation detail: " + detail);
+ log.debug("Validation code: " + code);
+
+ int users =
+ jsonResponse
+ .path("data")
+ .path("attributes")
+ .path("metadata")
+ .path("users")
+ .asInt(0);
+ applicationProperties.getEnterpriseEdition().setMaxUsers(users);
+ log.info(applicationProperties.toString());
+
+ } else {
+ log.error("Error validating license. Status code: " + response.statusCode());
+ }
+ return jsonResponse;
+ }
+
+ private boolean activateMachine(String licenseKey, String licenseId, String machineFingerprint)
+ throws Exception {
+ HttpClient client = HttpClient.newHttpClient();
+
+ String hostname;
+ try {
+ hostname = java.net.InetAddress.getLocalHost().getHostName();
+ } catch (Exception e) {
+ hostname = "Unknown";
+ }
+
+ JSONObject body =
+ new JSONObject()
+ .put(
+ "data",
+ new JSONObject()
+ .put("type", "machines")
+ .put(
+ "attributes",
+ new JSONObject()
+ .put("fingerprint", machineFingerprint)
+ .put(
+ "platform",
+ System.getProperty(
+ "os.name")) // Added
+ // platform
+ // parameter
+ .put(
+ "name",
+ hostname)) // Added name parameter
+ .put(
+ "relationships",
+ new JSONObject()
+ .put(
+ "license",
+ new JSONObject()
+ .put(
+ "data",
+ new JSONObject()
+ .put(
+ "type",
+ "licenses")
+ .put(
+ "id",
+ licenseId)))));
+
+ HttpRequest request =
+ HttpRequest.newBuilder()
+ .uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines"))
+ .header("Content-Type", "application/vnd.api+json")
+ .header("Accept", "application/vnd.api+json")
+ .header(
+ "Authorization",
+ "License " + licenseKey) // Keep the license key authentication
+ .POST(
+ HttpRequest.BodyPublishers.ofString(
+ body.toString())) // Send the JSON body
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ log.debug("activateMachine Response body: " + response.body());
+ if (response.statusCode() == 201) {
+ log.info("Machine activated successfully");
+ return true;
+ } else {
+ log.error(
+ "Error activating machine. Status code: {}, error: {}",
+ response.statusCode(),
+ response.body());
+
+ return false;
+ }
+ }
+
+ private String generateMachineFingerprint() {
+ return GeneralUtils.generateMachineFingerprint();
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/EE/LicenseKeyChecker.java b/src/main/java/stirling/software/SPDF/EE/LicenseKeyChecker.java
new file mode 100644
index 00000000000..3da7da05d9d
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/EE/LicenseKeyChecker.java
@@ -0,0 +1,59 @@
+package stirling.software.SPDF.EE;
+
+import java.io.IOException;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+import stirling.software.SPDF.model.ApplicationProperties;
+import stirling.software.SPDF.utils.GeneralUtils;
+
+@Component
+@Slf4j
+public class LicenseKeyChecker {
+
+ private final KeygenLicenseVerifier licenseService;
+
+ private final ApplicationProperties applicationProperties;
+
+ private boolean enterpriseEnbaledResult = false;
+
+ @Autowired
+ public LicenseKeyChecker(
+ KeygenLicenseVerifier licenseService, ApplicationProperties applicationProperties) {
+ this.licenseService = licenseService;
+ this.applicationProperties = applicationProperties;
+ }
+
+ @Scheduled(fixedRate = 604800000, initialDelay = 1000) // 7 days in milliseconds
+ public void checkLicensePeriodically() {
+ checkLicense();
+ }
+
+ private void checkLicense() {
+ if (!applicationProperties.getEnterpriseEdition().isEnabled()) {
+ enterpriseEnbaledResult = false;
+ } else {
+ enterpriseEnbaledResult =
+ licenseService.verifyLicense(
+ applicationProperties.getEnterpriseEdition().getKey());
+ if (enterpriseEnbaledResult) {
+ log.info("License key is valid.");
+ } else {
+ log.info("License key is invalid.");
+ }
+ }
+ }
+
+ public void updateLicenseKey(String newKey) throws IOException {
+ applicationProperties.getEnterpriseEdition().setKey(newKey);
+ GeneralUtils.saveKeyToConfig("EnterpriseEdition.key", newKey, false);
+ checkLicense();
+ }
+
+ public boolean getEnterpriseEnabledResult() {
+ return enterpriseEnbaledResult;
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/LibreOfficeListener.java b/src/main/java/stirling/software/SPDF/LibreOfficeListener.java
index 06e195126ca..549a009268a 100644
--- a/src/main/java/stirling/software/SPDF/LibreOfficeListener.java
+++ b/src/main/java/stirling/software/SPDF/LibreOfficeListener.java
@@ -11,6 +11,9 @@
import io.github.pixee.security.SystemCommand;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
public class LibreOfficeListener {
private static final Logger logger = LoggerFactory.getLogger(LibreOfficeListener.class);
@@ -31,7 +34,7 @@ public static LibreOfficeListener getInstance() {
private LibreOfficeListener() {}
private boolean isListenerRunning() {
- System.out.println("waiting for listener to start");
+ log.info("waiting for listener to start");
try (Socket socket = new Socket()) {
socket.connect(
new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second
diff --git a/src/main/java/stirling/software/SPDF/SPdfApplication.java b/src/main/java/stirling/software/SPDF/SPdfApplication.java
index 7149088b614..eddf7306c07 100644
--- a/src/main/java/stirling/software/SPDF/SPdfApplication.java
+++ b/src/main/java/stirling/software/SPDF/SPdfApplication.java
@@ -33,11 +33,15 @@ public class SPdfApplication {
@Autowired private Environment env;
@Autowired ApplicationProperties applicationProperties;
+ private static String baseUrlStatic;
private static String serverPortStatic;
+ @Value("${baseUrl:http://localhost}")
+ private String baseUrl;
+
@Value("${server.port:8080}")
public void setServerPortStatic(String port) {
- if (port.equalsIgnoreCase("auto")) {
+ if ("auto".equalsIgnoreCase(port)) {
// Use Spring Boot's automatic port assignment (server.port=0)
SPdfApplication.serverPortStatic =
"0"; // This will let Spring Boot assign an available port
@@ -65,12 +69,13 @@ private static boolean isPortAvailable(int port) {
@PostConstruct
public void init() {
+ baseUrlStatic = this.baseUrl;
// Check if the BROWSER_OPEN environment variable is set to true
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) {
try {
- String url = "http://localhost:" + getStaticPort();
+ String url = baseUrl + ":" + getStaticPort();
String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime();
@@ -78,9 +83,9 @@ public void init() {
// For Windows
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
} else if (os.contains("mac")) {
- rt.exec("open " + url);
+ SystemCommand.runCommand(rt, "open " + url);
} else if (os.contains("nix") || os.contains("nux")) {
- rt.exec("xdg-open " + url);
+ SystemCommand.runCommand(rt, "xdg-open " + url);
}
} catch (Exception e) {
logger.error("Error opening browser: {}", e.getMessage());
@@ -138,10 +143,18 @@ public static void main(String[] args) throws IOException, InterruptedException
private static void printStartupLogs() {
logger.info("Stirling-PDF Started.");
- String url = "http://localhost:" + getStaticPort();
+ String url = baseUrlStatic + ":" + getStaticPort();
logger.info("Navigate to {}", url);
}
+ public static String getStaticBaseUrl() {
+ return baseUrlStatic;
+ }
+
+ public String getNonStaticBaseUrl() {
+ return baseUrlStatic;
+ }
+
public static String getStaticPort() {
return serverPortStatic;
}
diff --git a/src/main/java/stirling/software/SPDF/config/AppConfig.java b/src/main/java/stirling/software/SPDF/config/AppConfig.java
index 6fc09917614..3391a50f009 100644
--- a/src/main/java/stirling/software/SPDF/config/AppConfig.java
+++ b/src/main/java/stirling/software/SPDF/config/AppConfig.java
@@ -160,4 +160,27 @@ public String impressum() {
public String accessibilityStatement() {
return applicationProperties.getLegal().getAccessibilityStatement();
}
+
+ @Bean(name = "analyticsPrompt")
+ public boolean analyticsPrompt() {
+ return applicationProperties.getSystem().getEnableAnalytics() == null
+ || "undefined".equals(applicationProperties.getSystem().getEnableAnalytics());
+ }
+
+ @Bean(name = "analyticsEnabled")
+ public boolean analyticsEnabled() {
+ if (applicationProperties.getEnterpriseEdition().isEnabled()) return true;
+ return applicationProperties.getSystem().getEnableAnalytics() != null
+ && Boolean.parseBoolean(applicationProperties.getSystem().getEnableAnalytics());
+ }
+
+ @Bean(name = "StirlingPDFLabel")
+ public String stirlingPDFLabel() {
+ return "Stirling-PDF" + " v" + appVersion();
+ }
+
+ @Bean(name = "UUID")
+ public String uuid() {
+ return applicationProperties.getAutomaticallyGenerated().getUUID();
+ }
}
diff --git a/src/main/java/stirling/software/SPDF/config/AppUpdateService.java b/src/main/java/stirling/software/SPDF/config/AppUpdateService.java
index 7fc87629083..3eb204887cf 100644
--- a/src/main/java/stirling/software/SPDF/config/AppUpdateService.java
+++ b/src/main/java/stirling/software/SPDF/config/AppUpdateService.java
@@ -5,6 +5,7 @@
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
+import stirling.software.SPDF.config.interfaces.ShowAdminInterface;
import stirling.software.SPDF.model.ApplicationProperties;
@Service
diff --git a/src/main/java/stirling/software/SPDF/config/InitialSetup.java b/src/main/java/stirling/software/SPDF/config/InitialSetup.java
new file mode 100644
index 00000000000..4be2e5e653f
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/InitialSetup.java
@@ -0,0 +1,65 @@
+package stirling.software.SPDF.config;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import io.micrometer.common.util.StringUtils;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import stirling.software.SPDF.model.ApplicationProperties;
+import stirling.software.SPDF.utils.GeneralUtils;
+
+@Component
+@Slf4j
+@Order(Ordered.HIGHEST_PRECEDENCE + 1)
+public class InitialSetup {
+
+ @Autowired private ApplicationProperties applicationProperties;
+
+ @PostConstruct
+ public void initUUIDKey() throws IOException {
+ String uuid = applicationProperties.getAutomaticallyGenerated().getUUID();
+ if (!GeneralUtils.isValidUUID(uuid)) {
+ uuid = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
+ GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.UUID", uuid);
+ applicationProperties.getAutomaticallyGenerated().setUUID(uuid);
+ }
+ }
+
+ @PostConstruct
+ public void initSecretKey() throws IOException {
+ String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
+ if (!GeneralUtils.isValidUUID(secretKey)) {
+ secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
+ GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.key", secretKey);
+ applicationProperties.getAutomaticallyGenerated().setKey(secretKey);
+ }
+ }
+
+ @PostConstruct
+ public void initLegalUrls() throws IOException {
+ // Initialize Terms and Conditions
+ String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
+ if (StringUtils.isEmpty(termsUrl)) {
+ String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions";
+ GeneralUtils.saveKeyToConfig("legal.termsAndConditions", defaultTermsUrl);
+ applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl);
+ }
+
+ // Initialize Privacy Policy
+ String privacyUrl = applicationProperties.getLegal().getPrivacyPolicy();
+ if (StringUtils.isEmpty(privacyUrl)) {
+ String defaultPrivacyUrl = "https://www.stirlingpdf.com/privacy-policy";
+ GeneralUtils.saveKeyToConfig("legal.privacyPolicy", defaultPrivacyUrl);
+ applicationProperties.getLegal().setPrivacyPolicy(defaultPrivacyUrl);
+ }
+ }
+
+}
+
+
diff --git a/src/main/java/stirling/software/SPDF/config/Beans.java b/src/main/java/stirling/software/SPDF/config/LocaleConfiguration.java
similarity index 97%
rename from src/main/java/stirling/software/SPDF/config/Beans.java
rename to src/main/java/stirling/software/SPDF/config/LocaleConfiguration.java
index 03084b2419f..a15d6c165b9 100644
--- a/src/main/java/stirling/software/SPDF/config/Beans.java
+++ b/src/main/java/stirling/software/SPDF/config/LocaleConfiguration.java
@@ -14,7 +14,7 @@
import stirling.software.SPDF.model.ApplicationProperties;
@Configuration
-public class Beans implements WebMvcConfigurer {
+public class LocaleConfiguration implements WebMvcConfigurer {
@Autowired ApplicationProperties applicationProperties;
diff --git a/src/main/java/stirling/software/SPDF/config/MetricsFilter.java b/src/main/java/stirling/software/SPDF/config/MetricsFilter.java
index c0231ca7f35..fba1ee9ce04 100644
--- a/src/main/java/stirling/software/SPDF/config/MetricsFilter.java
+++ b/src/main/java/stirling/software/SPDF/config/MetricsFilter.java
@@ -13,6 +13,7 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.utils.RequestUriUtils;
@Component
@@ -32,10 +33,11 @@ protected void doFilterInternal(
String uri = request.getRequestURI();
if (RequestUriUtils.isTrackableResource(request.getContextPath(), uri)) {
-
+ HttpSession session = request.getSession(false);
+ String sessionId = (session != null) ? session.getId() : "no-session";
Counter counter =
Counter.builder("http.requests")
- .tag("session", request.getSession().getId())
+ .tag("session", sessionId)
.tag("method", request.getMethod())
.tag("uri", uri)
.register(meterRegistry);
diff --git a/src/main/java/stirling/software/SPDF/config/PostHogConfig.java b/src/main/java/stirling/software/SPDF/config/PostHogConfig.java
new file mode 100644
index 00000000000..42d5b10146a
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/PostHogConfig.java
@@ -0,0 +1,34 @@
+package stirling.software.SPDF.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.posthog.java.PostHog;
+
+import jakarta.annotation.PreDestroy;
+
+@Configuration
+public class PostHogConfig {
+
+ @Value("${posthog.api.key}")
+ private String posthogApiKey;
+
+ @Value("${posthog.host}")
+ private String posthogHost;
+
+ private PostHog postHogClient;
+
+ @Bean
+ public PostHog postHogClient() {
+ postHogClient = new PostHog.Builder(posthogApiKey).host(posthogHost).build();
+ return postHogClient;
+ }
+
+ @PreDestroy
+ public void shutdownPostHog() {
+ if (postHogClient != null) {
+ postHogClient.shutdown();
+ }
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintBasedSessionFilter.java b/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintBasedSessionFilter.java
new file mode 100644
index 00000000000..3815b95bd27
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintBasedSessionFilter.java
@@ -0,0 +1,68 @@
+// package stirling.software.SPDF.config.fingerprint;
+//
+// import java.io.IOException;
+//
+// import org.springframework.beans.factory.annotation.Autowired;
+// import org.springframework.stereotype.Component;
+// import org.springframework.web.filter.OncePerRequestFilter;
+//
+// import jakarta.servlet.FilterChain;
+// import jakarta.servlet.ServletException;
+// import jakarta.servlet.http.HttpServletRequest;
+// import jakarta.servlet.http.HttpServletResponse;
+// import jakarta.servlet.http.HttpSession;
+// import lombok.extern.slf4j.Slf4j;
+// import stirling.software.SPDF.utils.RequestUriUtils;
+//
+//// @Component
+// @Slf4j
+// public class FingerprintBasedSessionFilter extends OncePerRequestFilter {
+// private final FingerprintGenerator fingerprintGenerator;
+// private final FingerprintBasedSessionManager sessionManager;
+//
+// @Autowired
+// public FingerprintBasedSessionFilter(
+// FingerprintGenerator fingerprintGenerator,
+// FingerprintBasedSessionManager sessionManager) {
+// this.fingerprintGenerator = fingerprintGenerator;
+// this.sessionManager = sessionManager;
+// }
+//
+// @Override
+// protected void doFilterInternal(
+// HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+// throws ServletException, IOException {
+//
+// if (RequestUriUtils.isStaticResource(request.getContextPath(), request.getRequestURI())) {
+// filterChain.doFilter(request, response);
+// return;
+// }
+//
+// String fingerprint = fingerprintGenerator.generateFingerprint(request);
+// log.debug("Generated fingerprint for request: {}", fingerprint);
+//
+// HttpSession session = request.getSession();
+// boolean isNewSession = session.isNew();
+// String sessionId = session.getId();
+//
+// if (isNewSession) {
+// log.info("New session created: {}", sessionId);
+// }
+//
+// if (!sessionManager.isFingerPrintAllowed(fingerprint)) {
+// log.info("Blocked fingerprint detected, redirecting: {}", fingerprint);
+// response.sendRedirect(request.getContextPath() + "/too-many-requests");
+// return;
+// }
+//
+// session.setAttribute("userFingerprint", fingerprint);
+// session.setAttribute(
+// FingerprintBasedSessionManager.STARTUP_TIMESTAMP,
+// FingerprintBasedSessionManager.APP_STARTUP_TIME);
+//
+// sessionManager.registerFingerprint(fingerprint, sessionId);
+//
+// log.debug("Proceeding with request: {}", request.getRequestURI());
+// filterChain.doFilter(request, response);
+// }
+// }
diff --git a/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintBasedSessionManager.java b/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintBasedSessionManager.java
new file mode 100644
index 00000000000..3854f62b130
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintBasedSessionManager.java
@@ -0,0 +1,134 @@
+// package stirling.software.SPDF.config.fingerprint;
+//
+// import java.util.Iterator;
+// import java.util.Map;
+// import java.util.concurrent.ConcurrentHashMap;
+// import java.util.concurrent.TimeUnit;
+//
+// import org.springframework.scheduling.annotation.Scheduled;
+// import org.springframework.stereotype.Component;
+//
+// import jakarta.servlet.http.HttpSession;
+// import jakarta.servlet.http.HttpSessionAttributeListener;
+// import jakarta.servlet.http.HttpSessionEvent;
+// import jakarta.servlet.http.HttpSessionListener;
+// import lombok.AllArgsConstructor;
+// import lombok.Data;
+// import lombok.extern.slf4j.Slf4j;
+//
+// @Slf4j
+// @Component
+// public class FingerprintBasedSessionManager
+// implements HttpSessionListener, HttpSessionAttributeListener {
+// private static final ConcurrentHashMap activeFingerprints =
+// new ConcurrentHashMap<>();
+//
+// // To be reduced in later version to 8~
+// private static final int MAX_ACTIVE_FINGERPRINTS = 30;
+//
+// static final String STARTUP_TIMESTAMP = "appStartupTimestamp";
+// static final long APP_STARTUP_TIME = System.currentTimeMillis();
+// private static final long FINGERPRINT_EXPIRATION = TimeUnit.MINUTES.toMillis(30);
+//
+// @Override
+// public void sessionCreated(HttpSessionEvent se) {
+// HttpSession session = se.getSession();
+// String sessionId = session.getId();
+// String fingerprint = (String) session.getAttribute("userFingerprint");
+//
+// if (fingerprint == null) {
+// log.warn("Session created without fingerprint: {}", sessionId);
+// return;
+// }
+//
+// synchronized (activeFingerprints) {
+// if (activeFingerprints.size() >= MAX_ACTIVE_FINGERPRINTS
+// && !activeFingerprints.containsKey(fingerprint)) {
+// log.info("Max fingerprints reached. Marking session as blocked: {}", sessionId);
+// session.setAttribute("blocked", true);
+// } else {
+// activeFingerprints.put(
+// fingerprint, new FingerprintInfo(sessionId, System.currentTimeMillis()));
+// log.info(
+// "New fingerprint registered: {}. Total active fingerprints: {}",
+// fingerprint,
+// activeFingerprints.size());
+// }
+// session.setAttribute(STARTUP_TIMESTAMP, APP_STARTUP_TIME);
+// }
+// }
+//
+// @Override
+// public void sessionDestroyed(HttpSessionEvent se) {
+// HttpSession session = se.getSession();
+// String fingerprint = (String) session.getAttribute("userFingerprint");
+//
+// if (fingerprint != null) {
+// synchronized (activeFingerprints) {
+// activeFingerprints.remove(fingerprint);
+// log.info(
+// "Fingerprint removed: {}. Total active fingerprints: {}",
+// fingerprint,
+// activeFingerprints.size());
+// }
+// }
+// }
+//
+// public boolean isFingerPrintAllowed(String fingerprint) {
+// synchronized (activeFingerprints) {
+// return activeFingerprints.size() < MAX_ACTIVE_FINGERPRINTS
+// || activeFingerprints.containsKey(fingerprint);
+// }
+// }
+//
+// public void registerFingerprint(String fingerprint, String sessionId) {
+// synchronized (activeFingerprints) {
+// activeFingerprints.put(
+// fingerprint, new FingerprintInfo(sessionId, System.currentTimeMillis()));
+// }
+// }
+//
+// public void unregisterFingerprint(String fingerprint) {
+// synchronized (activeFingerprints) {
+// activeFingerprints.remove(fingerprint);
+// }
+// }
+//
+// @Scheduled(fixedRate = 1800000) // Run every 30 mins
+// public void cleanupStaleFingerprints() {
+// log.info("Starting cleanup of stale fingerprints");
+// long now = System.currentTimeMillis();
+// int removedCount = 0;
+//
+// synchronized (activeFingerprints) {
+// Iterator> iterator =
+// activeFingerprints.entrySet().iterator();
+// while (iterator.hasNext()) {
+// Map.Entry entry = iterator.next();
+// FingerprintInfo info = entry.getValue();
+//
+// if (now - info.getLastAccessTime() > FINGERPRINT_EXPIRATION) {
+// iterator.remove();
+// removedCount++;
+// log.info("Removed stale fingerprint: {}", entry.getKey());
+// }
+// }
+// }
+//
+// log.info("Cleanup complete. Removed {} stale fingerprints", removedCount);
+// }
+//
+// public void updateLastAccessTime(String fingerprint) {
+// FingerprintInfo info = activeFingerprints.get(fingerprint);
+// if (info != null) {
+// info.setLastAccessTime(System.currentTimeMillis());
+// }
+// }
+//
+// @Data
+// @AllArgsConstructor
+// private static class FingerprintInfo {
+// private String sessionId;
+// private long lastAccessTime;
+// }
+// }
diff --git a/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintGenerator.java b/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintGenerator.java
new file mode 100644
index 00000000000..f1a65cf8c56
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/fingerprint/FingerprintGenerator.java
@@ -0,0 +1,77 @@
+// package stirling.software.SPDF.config.fingerprint;
+//
+// import java.security.MessageDigest;
+// import java.security.NoSuchAlgorithmException;
+//
+// import org.springframework.stereotype.Component;
+//
+// import jakarta.servlet.http.HttpServletRequest;
+//
+// @Component
+// public class FingerprintGenerator {
+//
+// public String generateFingerprint(HttpServletRequest request) {
+// if (request == null) {
+// return "";
+// }
+// StringBuilder fingerprintBuilder = new StringBuilder();
+//
+// // Add IP address
+// fingerprintBuilder.append(request.getRemoteAddr());
+//
+// // Add X-Forwarded-For header if present (for clients behind proxies)
+// String forwardedFor = request.getHeader("X-Forwarded-For");
+// if (forwardedFor != null) {
+// fingerprintBuilder.append(forwardedFor);
+// }
+//
+// // Add User-Agent
+// String userAgent = request.getHeader("User-Agent");
+// if (userAgent != null) {
+// fingerprintBuilder.append(userAgent);
+// }
+//
+// // Add Accept-Language header
+// String acceptLanguage = request.getHeader("Accept-Language");
+// if (acceptLanguage != null) {
+// fingerprintBuilder.append(acceptLanguage);
+// }
+//
+// // Add Accept header
+// String accept = request.getHeader("Accept");
+// if (accept != null) {
+// fingerprintBuilder.append(accept);
+// }
+//
+// // Add Connection header
+// String connection = request.getHeader("Connection");
+// if (connection != null) {
+// fingerprintBuilder.append(connection);
+// }
+//
+// // Add server port
+// fingerprintBuilder.append(request.getServerPort());
+//
+// // Add secure flag
+// fingerprintBuilder.append(request.isSecure());
+//
+// // Generate a hash of the fingerprint
+// return generateHash(fingerprintBuilder.toString());
+// }
+//
+// private String generateHash(String input) {
+// try {
+// MessageDigest digest = MessageDigest.getInstance("SHA-256");
+// byte[] hash = digest.digest(input.getBytes());
+// StringBuilder hexString = new StringBuilder();
+// for (byte b : hash) {
+// String hex = Integer.toHexString(0xff & b);
+// if (hex.length() == 1) hexString.append('0');
+// hexString.append(hex);
+// }
+// return hexString.toString();
+// } catch (NoSuchAlgorithmException e) {
+// throw new RuntimeException("Failed to generate fingerprint hash", e);
+// }
+// }
+// }
diff --git a/src/main/java/stirling/software/SPDF/config/DatabaseBackupInterface.java b/src/main/java/stirling/software/SPDF/config/interfaces/DatabaseBackupInterface.java
similarity index 85%
rename from src/main/java/stirling/software/SPDF/config/DatabaseBackupInterface.java
rename to src/main/java/stirling/software/SPDF/config/interfaces/DatabaseBackupInterface.java
index 267981d1888..3ad11f2a6c1 100644
--- a/src/main/java/stirling/software/SPDF/config/DatabaseBackupInterface.java
+++ b/src/main/java/stirling/software/SPDF/config/interfaces/DatabaseBackupInterface.java
@@ -1,4 +1,4 @@
-package stirling.software.SPDF.config;
+package stirling.software.SPDF.config.interfaces;
import java.io.IOException;
import java.util.List;
diff --git a/src/main/java/stirling/software/SPDF/config/ShowAdminInterface.java b/src/main/java/stirling/software/SPDF/config/interfaces/ShowAdminInterface.java
similarity index 69%
rename from src/main/java/stirling/software/SPDF/config/ShowAdminInterface.java
rename to src/main/java/stirling/software/SPDF/config/interfaces/ShowAdminInterface.java
index e49376e27c2..1bbebf5a998 100644
--- a/src/main/java/stirling/software/SPDF/config/ShowAdminInterface.java
+++ b/src/main/java/stirling/software/SPDF/config/interfaces/ShowAdminInterface.java
@@ -1,4 +1,4 @@
-package stirling.software.SPDF.config;
+package stirling.software.SPDF.config.interfaces;
public interface ShowAdminInterface {
default boolean getShowUpdateOnlyAdmins() {
diff --git a/src/main/java/stirling/software/SPDF/config/security/AppUpdateAuthService.java b/src/main/java/stirling/software/SPDF/config/security/AppUpdateAuthService.java
index 5a16aa308ab..57ce7b7d6c5 100644
--- a/src/main/java/stirling/software/SPDF/config/security/AppUpdateAuthService.java
+++ b/src/main/java/stirling/software/SPDF/config/security/AppUpdateAuthService.java
@@ -7,7 +7,7 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
-import stirling.software.SPDF.config.ShowAdminInterface;
+import stirling.software.SPDF.config.interfaces.ShowAdminInterface;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.UserRepository;
diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java
index d62956e1761..8d5aa76d015 100644
--- a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java
+++ b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java
@@ -1,27 +1,237 @@
package stirling.software.SPDF.config.security;
import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.core.io.Resource;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
+import com.coveo.saml.SamlClient;
+
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import stirling.software.SPDF.SPdfApplication;
+import stirling.software.SPDF.config.security.saml2.CertificateUtils;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
+import stirling.software.SPDF.model.ApplicationProperties;
+import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
+import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
+import stirling.software.SPDF.model.Provider;
+import stirling.software.SPDF.model.provider.UnsupportedProviderException;
+import stirling.software.SPDF.utils.UrlUtils;
+@Slf4j
+@AllArgsConstructor
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
+ private final ApplicationProperties applicationProperties;
+
@Override
public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
- if (request.getParameter("userIsDisabled") != null) {
- getRedirectStrategy()
- .sendRedirect(request, response, "/login?erroroauth=userIsDisabled");
- return;
+ if (!response.isCommitted()) {
+ // Handle user logout due to disabled account
+ if (request.getParameter("userIsDisabled") != null) {
+ response.sendRedirect(
+ request.getContextPath() + "/login?erroroauth=userIsDisabled");
+ return;
+ }
+ // Handle OAuth2 authentication error
+ if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
+ response.sendRedirect(
+ request.getContextPath() + "/login?erroroauth=userAlreadyExistsWeb");
+ return;
+ }
+ if (authentication != null) {
+ // Handle SAML2 logout redirection
+ if (authentication instanceof Saml2Authentication) {
+ getRedirect_saml2(request, response, authentication);
+ return;
+ }
+ // Handle OAuth2 logout redirection
+ else if (authentication instanceof OAuth2AuthenticationToken) {
+ getRedirect_oauth2(request, response, authentication);
+ return;
+ }
+ // Handle Username/Password logout
+ else if (authentication instanceof UsernamePasswordAuthenticationToken) {
+ getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
+ return;
+ }
+ // Handle unknown authentication types
+ else {
+ log.error(
+ "authentication class unknown: "
+ + authentication.getClass().getSimpleName());
+ getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
+ return;
+ }
+ } else {
+ // Redirect to login page after logout
+ getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
+ return;
+ }
}
+ }
+
+ // Redirect for SAML2 authentication logout
+ private void getRedirect_saml2(
+ HttpServletRequest request, HttpServletResponse response, Authentication authentication)
+ throws IOException {
+
+ SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
+ String registrationId = samlConf.getRegistrationId();
+
+ Saml2Authentication samlAuthentication = (Saml2Authentication) authentication;
+ CustomSaml2AuthenticatedPrincipal principal =
+ (CustomSaml2AuthenticatedPrincipal) samlAuthentication.getPrincipal();
+
+ String nameIdValue = principal.getName();
+
+ try {
+ // Read certificate from the resource
+ Resource certificateResource = samlConf.getSpCert();
+ X509Certificate certificate = CertificateUtils.readCertificate(certificateResource);
+
+ List certificates = new ArrayList<>();
+ certificates.add(certificate);
+
+ // Construct URLs required for SAML configuration
+ String serverUrl =
+ SPdfApplication.getStaticBaseUrl() + ":" + SPdfApplication.getStaticPort();
+
+ String relyingPartyIdentifier =
+ serverUrl + "/saml2/service-provider-metadata/" + registrationId;
+
+ String assertionConsumerServiceUrl = serverUrl + "/login/saml2/sso/" + registrationId;
+
+ String idpUrl = samlConf.getIdpSingleLogoutUrl();
+
+ String idpIssuer = samlConf.getIdpIssuer();
+
+ // Create SamlClient instance for SAML logout
+ SamlClient samlClient =
+ new SamlClient(
+ relyingPartyIdentifier,
+ assertionConsumerServiceUrl,
+ idpUrl,
+ idpIssuer,
+ certificates,
+ SamlClient.SamlIdpBinding.POST);
+
+ // Read private key for service provider
+ Resource privateKeyResource = samlConf.getPrivateKey();
+ RSAPrivateKey privateKey = CertificateUtils.readPrivateKey(privateKeyResource);
+
+ // Set service provider keys for the SamlClient
+ samlClient.setSPKeys(certificate, privateKey);
+
+ // Redirect to identity provider for logout
+ samlClient.redirectToIdentityProvider(response, null, nameIdValue);
+ } catch (Exception e) {
+ log.error(nameIdValue, e);
+ getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
+ }
+ }
+
+ // Redirect for OAuth2 authentication logout
+ private void getRedirect_oauth2(
+ HttpServletRequest request, HttpServletResponse response, Authentication authentication)
+ throws IOException {
+ String param = "logout=true";
+ String registrationId = null;
+ String issuer = null;
+ String clientId = null;
+ OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
+
+ if (authentication instanceof OAuth2AuthenticationToken) {
+ OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
+ registrationId = oauthToken.getAuthorizedClientRegistrationId();
+
+ try {
+ // Get OAuth2 provider details from configuration
+ Provider provider = oauth.getClient().get(registrationId);
+ issuer = provider.getIssuer();
+ clientId = provider.getClientId();
+ } catch (UnsupportedProviderException e) {
+ log.error(e.getMessage());
+ }
+ } else {
+ registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
+ issuer = oauth.getIssuer();
+ clientId = oauth.getClientId();
+ }
+ String errorMessage = "";
+ // Handle different error scenarios during logout
+ if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
+ param = "erroroauth=oauth2AuthenticationErrorWeb";
+ } else if ((errorMessage = request.getParameter("error")) != null) {
+ param = "error=" + sanitizeInput(errorMessage);
+ } else if ((errorMessage = request.getParameter("erroroauth")) != null) {
+ param = "erroroauth=" + sanitizeInput(errorMessage);
+ } else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
+ param = "error=oauth2AutoCreateDisabled";
+ } else if (request.getParameter("oauth2_admin_blocked_user") != null) {
+ param = "erroroauth=oauth2_admin_blocked_user";
+ } else if (request.getParameter("userIsDisabled") != null) {
+ param = "erroroauth=userIsDisabled";
+ } else if (request.getParameter("badcredentials") != null) {
+ param = "error=badcredentials";
+ }
+
+ String redirect_url = UrlUtils.getOrigin(request) + "/login?" + param;
+
+ // Redirect based on OAuth2 provider
+ switch (registrationId.toLowerCase()) {
+ case "keycloak":
+ // Add Keycloak specific logout URL if needed
+ String logoutUrl =
+ issuer
+ + "/protocol/openid-connect/logout"
+ + "?client_id="
+ + clientId
+ + "&post_logout_redirect_uri="
+ + response.encodeRedirectURL(redirect_url);
+ log.info("Redirecting to Keycloak logout URL: " + logoutUrl);
+ response.sendRedirect(logoutUrl);
+ break;
+ case "github":
+ // Add GitHub specific logout URL if needed
+ String githubLogoutUrl = "https://github.com/logout";
+ log.info("Redirecting to GitHub logout URL: " + githubLogoutUrl);
+ response.sendRedirect(githubLogoutUrl);
+ break;
+ case "google":
+ // Add Google specific logout URL if needed
+ // String googleLogoutUrl =
+ // "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
+ // + response.encodeRedirectURL(redirect_url);
+ log.info("Google does not have a specific logout URL");
+ // log.info("Redirecting to Google logout URL: " + googleLogoutUrl);
+ // response.sendRedirect(googleLogoutUrl);
+ // break;
+ default:
+ String defaultRedirectUrl = request.getContextPath() + "/login?" + param;
+ log.info("Redirecting to default logout URL: " + defaultRedirectUrl);
+ response.sendRedirect(defaultRedirectUrl);
+ break;
+ }
+ }
- getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
+ // Sanitize input to avoid potential security vulnerabilities
+ private String sanitizeInput(String input) {
+ return input.replaceAll("[^a-zA-Z0-9 ]", "");
}
}
diff --git a/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java b/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java
index 86afa0287af..babba64d280 100644
--- a/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java
+++ b/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java
@@ -1,6 +1,8 @@
package stirling.software.SPDF.config.security;
import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
@@ -14,9 +16,12 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.utils.RequestUriUtils;
+@Slf4j
@Component
public class FirstLoginFilter extends OncePerRequestFilter {
@@ -50,6 +55,22 @@ protected void doFilterInternal(
return;
}
}
+
+ if (log.isDebugEnabled()) {
+ HttpSession session = request.getSession(true);
+ SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
+ String creationTime = timeFormat.format(new Date(session.getCreationTime()));
+
+ log.debug(
+ "Request Info - New: {}, creationTimeSession {}, ID: {}, IP: {}, User-Agent: {}, Referer: {}, Request URL: {}",
+ session.isNew(),
+ creationTime,
+ session.getId(),
+ request.getRemoteAddr(),
+ request.getHeader("User-Agent"),
+ request.getHeader("Referer"),
+ request.getRequestURL().toString());
+ }
filterChain.doFilter(request, response);
}
}
diff --git a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java
index b7442c5b2f1..f43baf0a392 100644
--- a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java
+++ b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java
@@ -1,19 +1,14 @@
package stirling.software.SPDF.config.security;
import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.UUID;
-import org.simpleyaml.configuration.file.YamlFile;
-import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
-import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
-import stirling.software.SPDF.config.DatabaseBackupInterface;
+import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.Role;
@@ -39,15 +34,6 @@ public void init() throws IllegalArgumentException, IOException {
initializeInternalApiUser();
}
- @PostConstruct
- public void initSecretKey() throws IOException {
- String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
- if (!isValidUUID(secretKey)) {
- secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
- saveKeyToConfig(secretKey);
- }
- }
-
private void initializeAdminUser() throws IOException {
String initialUsername =
applicationProperties.getSecurity().getInitialLogin().getUsername();
@@ -89,33 +75,4 @@ private void initializeInternalApiUser() throws IllegalArgumentException, IOExce
log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
}
}
-
- private void saveKeyToConfig(String key) throws IOException {
- Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
-
- final YamlFile settingsYml = new YamlFile(path.toFile());
- DumperOptions yamlOptionssettingsYml =
- ((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
- yamlOptionssettingsYml.setSplitLines(false);
-
- settingsYml.loadWithComments();
-
- settingsYml
- .path("AutomaticallyGenerated.key")
- .set(key)
- .comment("# Automatically Generated Settings (Do Not Edit Directly)");
- settingsYml.save();
- }
-
- private boolean isValidUUID(String uuid) {
- if (uuid == null) {
- return false;
- }
- try {
- UUID.fromString(uuid);
- return true;
- } catch (IllegalArgumentException e) {
- return false;
- }
- }
}
diff --git a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java
index aa266d2f144..e6b149ba919 100644
--- a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java
+++ b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java
@@ -1,15 +1,17 @@
package stirling.software.SPDF.config.security;
+import java.security.cert.X509Certificate;
import java.util.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
+import org.springframework.core.io.Resource;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -24,20 +26,32 @@
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.registration.InMemoryRelyingPartyRegistrationRepository;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
-import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
+import stirling.software.SPDF.config.security.saml2.CertificateUtils;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationFailureHandler;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationSuccessHandler;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2ResponseAuthenticationConverter;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
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.provider.GithubProvider;
import stirling.software.SPDF.model.provider.GoogleProvider;
@@ -47,12 +61,11 @@
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
+@Slf4j
public class SecurityConfiguration {
@Autowired private CustomUserDetailsService userDetailsService;
- private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
-
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
@@ -75,11 +88,13 @@ public PasswordEncoder passwordEncoder() {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
if (loginEnabledValue) {
-
- http.csrf(csrf -> csrf.disable());
+ http.addFilterBefore(
+ userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+ if (applicationProperties.getSecurity().getCsrfDisabled()) {
+ http.csrf(csrf -> csrf.disable());
+ }
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
http.sessionManagement(
@@ -91,114 +106,142 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionRegistry(sessionRegistry)
.expiredUrl("/login?logout=true"));
- http.formLogin(
- formLogin ->
- formLogin
- .loginPage("/login")
- .successHandler(
- new CustomAuthenticationSuccessHandler(
- loginAttemptService, userService))
- .defaultSuccessUrl("/")
- .failureHandler(
- new CustomAuthenticationFailureHandler(
- loginAttemptService, userService))
- .permitAll())
- .requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()))
- .logout(
- logout ->
- logout.logoutRequestMatcher(
- new AntPathRequestMatcher("/logout"))
- .logoutSuccessHandler(new CustomLogoutSuccessHandler())
- .invalidateHttpSession(true) // Invalidate session
- .deleteCookies("JSESSIONID", "remember-me"))
- .rememberMe(
- rememberMeConfigurer ->
- rememberMeConfigurer // Use the configurator directly
- .key("uniqueAndSecret")
- .tokenRepository(persistentTokenRepository())
- .tokenValiditySeconds(1209600) // 2 weeks
- )
- .authorizeHttpRequests(
- authz ->
- authz.requestMatchers(
- req -> {
- String uri = req.getRequestURI();
- String contextPath = req.getContextPath();
-
- // Remove the context path from the URI
- String trimmedUri =
- uri.startsWith(contextPath)
- ? uri.substring(
- contextPath
- .length())
- : uri;
-
- return trimmedUri.startsWith("/login")
- || trimmedUri.startsWith("/oauth")
- || trimmedUri.endsWith(".svg")
- || trimmedUri.startsWith(
- "/register")
- || trimmedUri.startsWith("/error")
- || trimmedUri.startsWith("/images/")
- || trimmedUri.startsWith("/public/")
- || trimmedUri.startsWith("/css/")
- || trimmedUri.startsWith("/fonts/")
- || trimmedUri.startsWith("/js/")
- || trimmedUri.startsWith(
- "/api/v1/info/status");
- })
- .permitAll()
- .anyRequest()
- .authenticated());
+ http.authenticationProvider(daoAuthenticationProvider());
+ http.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()));
+ http.logout(
+ logout ->
+ logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
+ .logoutSuccessHandler(
+ new CustomLogoutSuccessHandler(applicationProperties))
+ .invalidateHttpSession(true) // Invalidate session
+ .deleteCookies("JSESSIONID", "remember-me"));
+ http.rememberMe(
+ rememberMeConfigurer ->
+ rememberMeConfigurer // Use the configurator directly
+ .key("uniqueAndSecret")
+ .tokenRepository(persistentTokenRepository())
+ .tokenValiditySeconds(1209600) // 2 weeks
+ );
+ http.authorizeHttpRequests(
+ authz ->
+ authz.requestMatchers(
+ req -> {
+ String uri = req.getRequestURI();
+ String contextPath = req.getContextPath();
+
+ // Remove the context path from the URI
+ String trimmedUri =
+ uri.startsWith(contextPath)
+ ? uri.substring(
+ contextPath.length())
+ : uri;
+
+ return trimmedUri.startsWith("/login")
+ || trimmedUri.startsWith("/oauth")
+ || trimmedUri.startsWith("/saml2")
+ || trimmedUri.endsWith(".svg")
+ || trimmedUri.startsWith("/register")
+ || trimmedUri.startsWith("/error")
+ || trimmedUri.startsWith("/images/")
+ || trimmedUri.startsWith("/public/")
+ || trimmedUri.startsWith("/css/")
+ || trimmedUri.startsWith("/fonts/")
+ || trimmedUri.startsWith("/js/")
+ || trimmedUri.startsWith(
+ "/api/v1/info/status");
+ })
+ .permitAll()
+ .anyRequest()
+ .authenticated());
+
+ // Handle User/Password Logins
+ if (applicationProperties.getSecurity().isUserPass()) {
+ http.formLogin(
+ formLogin ->
+ formLogin
+ .loginPage("/login")
+ .successHandler(
+ new CustomAuthenticationSuccessHandler(
+ loginAttemptService, userService))
+ .failureHandler(
+ new CustomAuthenticationFailureHandler(
+ loginAttemptService, userService))
+ .defaultSuccessUrl("/")
+ .permitAll());
+ }
// Handle OAUTH2 Logins
- if (applicationProperties.getSecurity().getOauth2() != null
- && applicationProperties.getSecurity().getOauth2().getEnabled()
- && !applicationProperties
- .getSecurity()
- .getLoginMethod()
- .equalsIgnoreCase("normal")) {
+ if (applicationProperties.getSecurity().isOauth2Activ()) {
http.oauth2Login(
- oauth2 ->
- oauth2.loginPage("/oauth2")
- /*
- This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database.
- If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser'
- is set as true, else login fails with an error message advising the same.
- */
+ oauth2 ->
+ oauth2.loginPage("/oauth2")
+ /*
+ This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database.
+ If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser'
+ is set as true, else login fails with an error message advising the same.
+ */
+ .successHandler(
+ new CustomOAuth2AuthenticationSuccessHandler(
+ loginAttemptService,
+ applicationProperties,
+ userService))
+ .failureHandler(
+ new CustomOAuth2AuthenticationFailureHandler())
+ // Add existing Authorities from the database
+ .userInfoEndpoint(
+ userInfoEndpoint ->
+ userInfoEndpoint
+ .oidcUserService(
+ new CustomOAuth2UserService(
+ applicationProperties,
+ userService,
+ loginAttemptService))
+ .userAuthoritiesMapper(
+ userAuthoritiesMapper()))
+ .permitAll());
+ }
+
+ // Handle SAML
+ if (applicationProperties.getSecurity().isSaml2Activ() && applicationProperties.getSystem().getEnableAlphaFunctionality()) {
+ http.authenticationProvider(samlAuthenticationProvider());
+ http.saml2Login(
+ saml2 ->
+ saml2.loginPage("/saml2")
.successHandler(
- new CustomOAuth2AuthenticationSuccessHandler(
+ new CustomSaml2AuthenticationSuccessHandler(
loginAttemptService,
applicationProperties,
userService))
.failureHandler(
- new CustomOAuth2AuthenticationFailureHandler())
- // Add existing Authorities from the database
- .userInfoEndpoint(
- userInfoEndpoint ->
- userInfoEndpoint
- .oidcUserService(
- new CustomOAuth2UserService(
- applicationProperties,
- userService,
- loginAttemptService))
- .userAuthoritiesMapper(
- userAuthoritiesMapper())))
- .logout(
- logout ->
- logout.logoutSuccessHandler(
- new CustomOAuth2LogoutSuccessHandler(
- applicationProperties)));
+ new CustomSaml2AuthenticationFailureHandler())
+ .permitAll())
+ .addFilterBefore(
+ userAuthenticationFilter, Saml2WebSsoAuthenticationFilter.class);
}
} else {
- http.csrf(csrf -> csrf.disable())
- .authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
+ if (applicationProperties.getSecurity().getCsrfDisabled()) {
+ http.csrf(csrf -> csrf.disable());
+ }
+ http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
}
return http.build();
}
+ @Bean
+ @ConditionalOnProperty(
+ name = "security.saml2.enabled",
+ havingValue = "true",
+ matchIfMissing = false)
+ public AuthenticationProvider samlAuthenticationProvider() {
+ OpenSaml4AuthenticationProvider authenticationProvider =
+ new OpenSaml4AuthenticationProvider();
+ authenticationProvider.setResponseAuthenticationConverter(
+ new CustomSaml2ResponseAuthenticationConverter(userService));
+ return authenticationProvider;
+ }
+
// Client Registration Repository for OAUTH2 OIDC Login
@Bean
@ConditionalOnProperty(
@@ -214,7 +257,7 @@ public ClientRegistrationRepository clientRegistrationRepository() {
keycloakClientRegistration().ifPresent(registrations::add);
if (registrations.isEmpty()) {
- logger.error("At least one OAuth2 provider must be configured");
+ log.error("At least one OAuth2 provider must be configured");
System.exit(1);
}
@@ -275,6 +318,7 @@ private Optional keycloakClientRegistration() {
}
private Optional githubClientRegistration() {
+
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (oauth == null || !oauth.getEnabled()) {
return Optional.empty();
@@ -329,6 +373,52 @@ private Optional oidcClientRegistration() {
.build());
}
+ @Bean
+ @ConditionalOnProperty(
+ name = "security.saml2.enabled",
+ havingValue = "true",
+ matchIfMissing = false)
+ public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
+
+ SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
+
+ Resource privateKeyResource = samlConf.getPrivateKey();
+
+ Resource certificateResource = samlConf.getSpCert();
+
+ Saml2X509Credential signingCredential =
+ new Saml2X509Credential(
+ CertificateUtils.readPrivateKey(privateKeyResource),
+ CertificateUtils.readCertificate(certificateResource),
+ Saml2X509CredentialType.SIGNING);
+
+ X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
+
+ Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
+
+ RelyingPartyRegistration rp =
+ RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
+ .signingX509Credentials((c) -> c.add(signingCredential))
+ .assertingPartyDetails(
+ (details) ->
+ details.entityId(samlConf.getIdpIssuer())
+ .singleSignOnServiceLocation(
+ samlConf.getIdpSingleLoginUrl())
+ .verificationX509Credentials(
+ (c) -> c.add(verificationCredential))
+ .wantAuthnRequestsSigned(true))
+ .build();
+ return new InMemoryRelyingPartyRegistrationRepository(rp);
+ }
+
+ @Bean
+ public DaoAuthenticationProvider daoAuthenticationProvider() {
+ DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+ provider.setUserDetailsService(userDetailsService);
+ provider.setPasswordEncoder(passwordEncoder());
+ 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.
@@ -386,4 +476,14 @@ public PersistentTokenRepository persistentTokenRepository() {
public boolean activSecurity() {
return true;
}
+
+ // // Only Dev test
+ // @Bean
+ // public WebSecurityCustomizer webSecurityCustomizer() {
+ // return (web) ->
+ // web.ignoring()
+ // .requestMatchers(
+ // "/css/**", "/images/**", "/js/**", "/**.svg",
+ // "/pdfjs-legacy/**");
+ // }
}
diff --git a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java
index 79b285211d0..4b62f6d2992 100644
--- a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java
+++ b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java
@@ -5,7 +5,6 @@
import java.util.Optional;
import java.util.stream.Collectors;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
@@ -23,6 +22,7 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
import stirling.software.SPDF.model.User;
@@ -30,13 +30,18 @@
@Component
public class UserAuthenticationFilter extends OncePerRequestFilter {
- @Autowired @Lazy private UserService userService;
-
- @Autowired private SessionPersistentRegistry sessionPersistentRegistry;
-
- @Autowired
- @Qualifier("loginEnabled")
- public boolean loginEnabledValue;
+ private final UserService userService;
+ private final SessionPersistentRegistry sessionPersistentRegistry;
+ private final boolean loginEnabledValue;
+
+ public UserAuthenticationFilter(
+ @Lazy UserService userService,
+ SessionPersistentRegistry sessionPersistentRegistry,
+ @Qualifier("loginEnabled") boolean loginEnabledValue) {
+ this.userService = userService;
+ this.sessionPersistentRegistry = sessionPersistentRegistry;
+ this.loginEnabledValue = loginEnabledValue;
+ }
@Override
protected void doFilterInternal(
@@ -51,6 +56,19 @@ protected void doFilterInternal(
String requestURI = request.getRequestURI();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ // Check for session expiration (unsure if needed)
+ // if (authentication != null && authentication.isAuthenticated()) {
+ // String sessionId = request.getSession().getId();
+ // SessionInformation sessionInfo =
+ // sessionPersistentRegistry.getSessionInformation(sessionId);
+ //
+ // if (sessionInfo != null && sessionInfo.isExpired()) {
+ // SecurityContextHolder.clearContext();
+ // response.sendRedirect(request.getContextPath() + "/login?expired=true");
+ // return;
+ // }
+ // }
+
// Check for API key in the request headers if no authentication exists
if (authentication == null || !authentication.isAuthenticated()) {
String apiKey = request.getHeader("X-API-Key");
@@ -94,7 +112,9 @@ protected void doFilterInternal(
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter()
.write(
- "Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternatively you can disable authentication if this is unexpected");
+ "Authentication required. Please provide a X-API-KEY in request header.\n"
+ + "This is found in Settings -> Account Settings -> API Key\n"
+ + "Alternatively you can disable authentication if this is unexpected");
return;
}
}
@@ -107,6 +127,8 @@ protected void doFilterInternal(
username = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
username = ((OAuth2User) principal).getName();
+ } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
+ username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
} else if (principal instanceof String) {
username = (String) principal;
}
diff --git a/src/main/java/stirling/software/SPDF/config/security/UserService.java b/src/main/java/stirling/software/SPDF/config/security/UserService.java
index ece81355501..c49002d64a6 100644
--- a/src/main/java/stirling/software/SPDF/config/security/UserService.java
+++ b/src/main/java/stirling/software/SPDF/config/security/UserService.java
@@ -19,7 +19,8 @@
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
-import stirling.software.SPDF.config.DatabaseBackupInterface;
+import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.AuthenticationType;
@@ -44,6 +45,7 @@ public class UserService implements UserServiceInterface {
@Autowired DatabaseBackupInterface databaseBackupHelper;
+
// Handle OAUTH2 login and user auto creation.
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser)
throws IllegalArgumentException, IOException {
@@ -334,6 +336,10 @@ public void invalidateUserSessions(String username) {
} else if (principal instanceof OAuth2User) {
OAuth2User oAuth2User = (OAuth2User) principal;
usernameP = oAuth2User.getName();
+ } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
+ CustomSaml2AuthenticatedPrincipal saml2User =
+ (CustomSaml2AuthenticatedPrincipal) principal;
+ usernameP = saml2User.getName();
} else if (principal instanceof String) {
usernameP = (String) principal;
}
@@ -353,4 +359,9 @@ public String getCurrentUsername() {
return principal.toString();
}
}
+
+ @Override
+ public long getTotalUsersCount() {
+ return userRepository.count();
+ }
}
diff --git a/src/main/java/stirling/software/SPDF/config/security/database/DatabaseBackupHelper.java b/src/main/java/stirling/software/SPDF/config/security/database/DatabaseBackupHelper.java
index 3fd7993ff08..6ccd0ac3faf 100644
--- a/src/main/java/stirling/software/SPDF/config/security/database/DatabaseBackupHelper.java
+++ b/src/main/java/stirling/software/SPDF/config/security/database/DatabaseBackupHelper.java
@@ -24,7 +24,7 @@
import org.springframework.context.annotation.Configuration;
import lombok.extern.slf4j.Slf4j;
-import stirling.software.SPDF.config.DatabaseBackupInterface;
+import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
import stirling.software.SPDF.utils.FileInfo;
@Slf4j
diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationFailureHandler.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationFailureHandler.java
index ebbe08f4ec9..88905f76415 100644
--- a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationFailureHandler.java
+++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationFailureHandler.java
@@ -51,8 +51,7 @@ public void onAuthenticationFailure(
}
log.error("OAuth2 Authentication error: " + errorCode);
log.error("OAuth2AuthenticationException", exception);
- getRedirectStrategy()
- .sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
+ getRedirectStrategy().sendRedirect(request, response, "/login?erroroauth=" + errorCode);
return;
}
log.error("Unhandled authentication exception", exception);
diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java
index c8c3f2174d4..36c4cfb366c 100644
--- a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java
+++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java
@@ -75,6 +75,11 @@ public void onAuthenticationSuccess(
throw new LockedException(
"Your account has been locked due to too many failed login attempts.");
}
+ if (userService.isUserDisabled(username)) {
+ getRedirectStrategy()
+ .sendRedirect(request, response, "/logout?userIsDisabled=true");
+ return;
+ }
if (userService.usernameExistsIgnoreCase(username)
&& userService.hasPassword(username)
&& !userService.isAuthenticationTypeByUsername(
diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2LogoutSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2LogoutSuccessHandler.java
deleted file mode 100644
index 5bbff53f623..00000000000
--- a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2LogoutSuccessHandler.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package stirling.software.SPDF.config.security.oauth2;
-
-import java.io.IOException;
-
-import org.springframework.security.core.Authentication;
-import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
-import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
-
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import lombok.extern.slf4j.Slf4j;
-import stirling.software.SPDF.model.ApplicationProperties;
-import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
-import stirling.software.SPDF.model.Provider;
-import stirling.software.SPDF.model.provider.UnsupportedProviderException;
-import stirling.software.SPDF.utils.UrlUtils;
-
-@Slf4j
-public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
-
- private final ApplicationProperties applicationProperties;
-
- public CustomOAuth2LogoutSuccessHandler(ApplicationProperties applicationProperties) {
- this.applicationProperties = applicationProperties;
- }
-
- @Override
- public void onLogoutSuccess(
- HttpServletRequest request, HttpServletResponse response, Authentication authentication)
- throws IOException, ServletException {
- String param = "logout=true";
- String registrationId = null;
- String issuer = null;
- String clientId = null;
-
- if (authentication == null) {
- if (request.getParameter("userIsDisabled") != null) {
- response.sendRedirect(
- request.getContextPath() + "/login?erroroauth=userIsDisabled");
- } else {
- super.onLogoutSuccess(request, response, authentication);
- }
- return;
- }
- OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
-
- if (authentication instanceof OAuth2AuthenticationToken) {
- OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
- registrationId = oauthToken.getAuthorizedClientRegistrationId();
-
- try {
- Provider provider = oauth.getClient().get(registrationId);
- issuer = provider.getIssuer();
- clientId = provider.getClientId();
- } catch (UnsupportedProviderException e) {
- log.error(e.getMessage());
- }
- } else {
- registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
- issuer = oauth.getIssuer();
- clientId = oauth.getClientId();
- }
- String errorMessage = "";
- if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
- param = "erroroauth=oauth2AuthenticationErrorWeb";
- } else if ((errorMessage = request.getParameter("error")) != null) {
- param = "error=" + sanitizeInput(errorMessage);
- } else if ((errorMessage = request.getParameter("erroroauth")) != null) {
- param = "erroroauth=" + sanitizeInput(errorMessage);
- } else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
- param = "error=oauth2AutoCreateDisabled";
- } else if (request.getParameter("oauth2_admin_blocked_user") != null) {
- param = "erroroauth=oauth2_admin_blocked_user";
- } else if (request.getParameter("userIsDisabled") != null) {
- param = "erroroauth=userIsDisabled";
- } else if (request.getParameter("badcredentials") != null) {
- param = "error=badcredentials";
- }
-
- String redirect_url = UrlUtils.getOrigin(request) + "/login?" + param;
-
- switch (registrationId.toLowerCase()) {
- case "keycloak":
- // Add Keycloak specific logout URL if needed
- String logoutUrl =
- issuer
- + "/protocol/openid-connect/logout"
- + "?client_id="
- + clientId
- + "&post_logout_redirect_uri="
- + response.encodeRedirectURL(redirect_url);
- log.info("Redirecting to Keycloak logout URL: " + logoutUrl);
- response.sendRedirect(logoutUrl);
- break;
- case "github":
- // Add GitHub specific logout URL if needed
- String githubLogoutUrl = "https://github.com/logout";
- log.info("Redirecting to GitHub logout URL: " + githubLogoutUrl);
- response.sendRedirect(githubLogoutUrl);
- break;
- case "google":
- // Add Google specific logout URL if needed
- // String googleLogoutUrl =
- // "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
- // + response.encodeRedirectURL(redirect_url);
- log.info("Google does not have a specific logout URL");
- // log.info("Redirecting to Google logout URL: " + googleLogoutUrl);
- // response.sendRedirect(googleLogoutUrl);
- // break;
- default:
- String defaultRedirectUrl = request.getContextPath() + "/login?" + param;
- log.info("Redirecting to default logout URL: " + defaultRedirectUrl);
- response.sendRedirect(defaultRedirectUrl);
- break;
- }
- }
-
- private String sanitizeInput(String input) {
- return input.replaceAll("[^a-zA-Z0-9 ]", "");
- }
-}
diff --git a/src/main/java/stirling/software/SPDF/config/security/saml2/CertificateUtils.java b/src/main/java/stirling/software/SPDF/config/security/saml2/CertificateUtils.java
new file mode 100644
index 00000000000..6a01ce0aaa9
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/security/saml2/CertificateUtils.java
@@ -0,0 +1,48 @@
+package stirling.software.SPDF.config.security.saml2;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+import org.springframework.core.io.Resource;
+import org.springframework.util.FileCopyUtils;
+
+public class CertificateUtils {
+
+ public static X509Certificate readCertificate(Resource certificateResource) throws Exception {
+ String certificateString =
+ new String(
+ FileCopyUtils.copyToByteArray(certificateResource.getInputStream()),
+ StandardCharsets.UTF_8);
+ String certContent =
+ certificateString
+ .replace("-----BEGIN CERTIFICATE-----", "")
+ .replace("-----END CERTIFICATE-----", "")
+ .replaceAll("\\R", "")
+ .replaceAll("\\s+", "");
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ byte[] decodedCert = Base64.getDecoder().decode(certContent);
+ return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(decodedCert));
+ }
+
+ public static RSAPrivateKey readPrivateKey(Resource privateKeyResource) throws Exception {
+ String privateKeyString =
+ new String(
+ FileCopyUtils.copyToByteArray(privateKeyResource.getInputStream()),
+ StandardCharsets.UTF_8);
+ String privateKeyContent =
+ privateKeyString
+ .replace("-----BEGIN PRIVATE KEY-----", "")
+ .replace("-----END PRIVATE KEY-----", "")
+ .replaceAll("\\R", "")
+ .replaceAll("\\s+", "");
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ byte[] decodedKey = Base64.getDecoder().decode(privateKeyContent);
+ return (RSAPrivateKey) kf.generatePrivate(new PKCS8EncodedKeySpec(decodedKey));
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticatedPrincipal.java b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticatedPrincipal.java
new file mode 100644
index 00000000000..efd0dc19917
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticatedPrincipal.java
@@ -0,0 +1,45 @@
+package stirling.software.SPDF.config.security.saml2;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
+
+public class CustomSaml2AuthenticatedPrincipal
+ implements Saml2AuthenticatedPrincipal, Serializable {
+
+ private final String name;
+ private final Map> attributes;
+ private final String nameId;
+ private final List sessionIndexes;
+
+ public CustomSaml2AuthenticatedPrincipal(
+ String name,
+ Map> attributes,
+ String nameId,
+ List sessionIndexes) {
+ this.name = name;
+ this.attributes = attributes;
+ this.nameId = nameId;
+ this.sessionIndexes = sessionIndexes;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public Map> getAttributes() {
+ return this.attributes;
+ }
+
+ public String getNameId() {
+ return this.nameId;
+ }
+
+ public List getSessionIndexes() {
+ return this.sessionIndexes;
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationFailureHandler.java b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationFailureHandler.java
new file mode 100644
index 00000000000..3b652d3588b
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationFailureHandler.java
@@ -0,0 +1,38 @@
+package stirling.software.SPDF.config.security.saml2;
+
+import java.io.IOException;
+
+import org.springframework.security.authentication.ProviderNotFoundException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.saml2.core.Saml2Error;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
+
+ @Override
+ public void onAuthenticationFailure(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ AuthenticationException exception)
+ throws IOException, ServletException {
+ if (exception instanceof Saml2AuthenticationException) {
+ Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
+ getRedirectStrategy()
+ .sendRedirect(request, response, "/login?erroroauth=" + error.getErrorCode());
+ } else if (exception instanceof ProviderNotFoundException) {
+ getRedirectStrategy()
+ .sendRedirect(
+ request,
+ response,
+ "/login?erroroauth=not_authentication_provider_found");
+ }
+ log.error("AuthenticationException: " + exception);
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationSuccessHandler.java
new file mode 100644
index 00000000000..d4b917581cf
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationSuccessHandler.java
@@ -0,0 +1,91 @@
+package stirling.software.SPDF.config.security.saml2;
+
+import java.io.IOException;
+
+import org.springframework.security.authentication.LockedException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+import org.springframework.security.web.savedrequest.SavedRequest;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import lombok.AllArgsConstructor;
+import stirling.software.SPDF.config.security.LoginAttemptService;
+import stirling.software.SPDF.config.security.UserService;
+import stirling.software.SPDF.model.ApplicationProperties;
+import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
+import stirling.software.SPDF.model.AuthenticationType;
+import stirling.software.SPDF.utils.RequestUriUtils;
+
+@AllArgsConstructor
+public class CustomSaml2AuthenticationSuccessHandler
+ extends SavedRequestAwareAuthenticationSuccessHandler {
+
+ private LoginAttemptService loginAttemptService;
+
+ private ApplicationProperties applicationProperties;
+ private UserService userService;
+
+ @Override
+ public void onAuthenticationSuccess(
+ HttpServletRequest request, HttpServletResponse response, Authentication authentication)
+ throws ServletException, IOException {
+
+ Object principal = authentication.getPrincipal();
+
+ if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
+ String username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
+ // Get the saved request
+ HttpSession session = request.getSession(false);
+ String contextPath = request.getContextPath();
+ SavedRequest savedRequest =
+ (session != null)
+ ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
+ : null;
+
+ if (savedRequest != null
+ && !RequestUriUtils.isStaticResource(
+ contextPath, savedRequest.getRedirectUrl())) {
+ // Redirect to the original destination
+ super.onAuthenticationSuccess(request, response, authentication);
+ } else {
+ SAML2 saml2 = applicationProperties.getSecurity().getSaml2();
+
+ if (loginAttemptService.isBlocked(username)) {
+ if (session != null) {
+ session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
+ }
+ throw new LockedException(
+ "Your account has been locked due to too many failed login attempts.");
+ }
+ if (userService.usernameExistsIgnoreCase(username)
+ && userService.hasPassword(username)
+ && !userService.isAuthenticationTypeByUsername(
+ username, AuthenticationType.OAUTH2)
+ && saml2.getAutoCreateUser()) {
+ response.sendRedirect(
+ contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
+ return;
+ }
+ try {
+ if (saml2.getBlockRegistration()
+ && !userService.usernameExistsIgnoreCase(username)) {
+ response.sendRedirect(
+ contextPath + "/login?erroroauth=oauth2_admin_blocked_user");
+ return;
+ }
+ userService.processOAuth2PostLogin(username, saml2.getAutoCreateUser());
+ response.sendRedirect(contextPath + "/");
+ return;
+ } catch (IllegalArgumentException e) {
+ response.sendRedirect(contextPath + "/logout?invalidUsername=true");
+ return;
+ }
+ }
+ } else {
+ super.onAuthenticationSuccess(request, response, authentication);
+ }
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2ResponseAuthenticationConverter.java b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2ResponseAuthenticationConverter.java
new file mode 100644
index 00000000000..b447eb8970b
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2ResponseAuthenticationConverter.java
@@ -0,0 +1,86 @@
+package stirling.software.SPDF.config.security.saml2;
+
+import java.util.*;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.saml2.core.Assertion;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeStatement;
+import org.opensaml.saml.saml2.core.AuthnStatement;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken;
+import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+import stirling.software.SPDF.config.security.UserService;
+import stirling.software.SPDF.model.User;
+
+@Component
+@Slf4j
+public class CustomSaml2ResponseAuthenticationConverter
+ implements Converter {
+
+ private UserService userService;
+
+ public CustomSaml2ResponseAuthenticationConverter(UserService userService) {
+ this.userService = userService;
+ }
+
+ @Override
+ public Saml2Authentication convert(ResponseToken responseToken) {
+ // Extract the assertion from the response
+ Assertion assertion = responseToken.getResponse().getAssertions().get(0);
+
+ // Extract the NameID
+ String nameId = assertion.getSubject().getNameID().getValue();
+
+ Optional userOpt = userService.findByUsernameIgnoreCase(nameId);
+ SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
+ if (userOpt.isPresent()) {
+ User user = userOpt.get();
+ if (user != null) {
+ simpleGrantedAuthority =
+ new SimpleGrantedAuthority(userService.findRole(user).getAuthority());
+ }
+ }
+
+ // Extract the SessionIndexes
+ List sessionIndexes = new ArrayList<>();
+ for (AuthnStatement authnStatement : assertion.getAuthnStatements()) {
+ sessionIndexes.add(authnStatement.getSessionIndex());
+ }
+
+ // Extract the Attributes
+ Map> attributes = extractAttributes(assertion);
+
+ // Create the custom principal
+ CustomSaml2AuthenticatedPrincipal principal =
+ new CustomSaml2AuthenticatedPrincipal(nameId, attributes, nameId, sessionIndexes);
+
+ // Create the Saml2Authentication
+ return new Saml2Authentication(
+ principal,
+ responseToken.getToken().getSaml2Response(),
+ Collections.singletonList(simpleGrantedAuthority));
+ }
+
+ private Map> extractAttributes(Assertion assertion) {
+ Map> attributes = new HashMap<>();
+ for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
+ for (Attribute attribute : attributeStatement.getAttributes()) {
+ String attributeName = attribute.getName();
+ List values = new ArrayList<>();
+ for (XMLObject xmlObject : attribute.getAttributeValues()) {
+ log.info("BOOL: " + ((XSBoolean) xmlObject).getValue());
+ values.add(((XSString) xmlObject).getValue());
+ }
+ attributes.put(attributeName, values);
+ }
+ }
+ return attributes;
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/config/security/session/CustomHttpSessionListener.java b/src/main/java/stirling/software/SPDF/config/security/session/CustomHttpSessionListener.java
index a4129e6a379..305684b94a5 100644
--- a/src/main/java/stirling/software/SPDF/config/security/session/CustomHttpSessionListener.java
+++ b/src/main/java/stirling/software/SPDF/config/security/session/CustomHttpSessionListener.java
@@ -11,16 +11,19 @@
@Slf4j
public class CustomHttpSessionListener implements HttpSessionListener {
- @Autowired private SessionPersistentRegistry sessionPersistentRegistry;
+ private SessionPersistentRegistry sessionPersistentRegistry;
- @Override
- public void sessionCreated(HttpSessionEvent se) {
- log.info("Session created: " + se.getSession().getId());
+ @Autowired
+ public CustomHttpSessionListener(SessionPersistentRegistry sessionPersistentRegistry) {
+ super();
+ this.sessionPersistentRegistry = sessionPersistentRegistry;
}
+ @Override
+ public void sessionCreated(HttpSessionEvent se) {}
+
@Override
public void sessionDestroyed(HttpSessionEvent se) {
- log.info("Session destroyed: " + se.getSession().getId());
sessionPersistentRegistry.expireSession(se.getSession().getId());
}
}
diff --git a/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java b/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java
index bb63a8b5e2c..045b6375f87 100644
--- a/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java
+++ b/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java
@@ -16,6 +16,7 @@
import org.springframework.stereotype.Component;
import jakarta.transaction.Transactional;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.model.SessionEntity;
@Component
@@ -50,6 +51,8 @@ public List getAllSessions(
principalName = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName();
+ } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
+ principalName = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
} else if (principal instanceof String) {
principalName = (String) principal;
}
@@ -79,11 +82,21 @@ public void registerNewSession(String sessionId, Object principal) {
principalName = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName();
+ } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
+ principalName = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
} else if (principal instanceof String) {
principalName = (String) principal;
}
if (principalName != null) {
+ // Clear old sessions for the principal (unsure if needed)
+ // List existingSessions =
+ // sessionRepository.findByPrincipalName(principalName);
+ // for (SessionEntity session : existingSessions) {
+ // session.setExpired(true);
+ // sessionRepository.save(session);
+ // }
+
SessionEntity sessionEntity = new SessionEntity();
sessionEntity.setSessionId(sessionId);
sessionEntity.setPrincipalName(principalName);
diff --git a/src/main/java/stirling/software/SPDF/controller/api/CropController.java b/src/main/java/stirling/software/SPDF/controller/api/CropController.java
index 551cd72dae6..d386d1ce575 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/CropController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/CropController.java
@@ -25,6 +25,7 @@
import stirling.software.SPDF.model.api.general.CropPdfForm;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
+import stirling.software.SPDF.service.PostHogService;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@@ -36,9 +37,13 @@ public class CropController {
private final CustomPDDocumentFactory pdfDocumentFactory;
+ private final PostHogService postHogService;
+
@Autowired
- public CropController(CustomPDDocumentFactory pdfDocumentFactory) {
+ public CropController(
+ CustomPDDocumentFactory pdfDocumentFactory, PostHogService postHogService) {
this.pdfDocumentFactory = pdfDocumentFactory;
+ this.postHogService = postHogService;
}
@PostMapping(value = "/crop", consumes = "multipart/form-data")
diff --git a/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java b/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java
index 302b00f4cad..92bd3bace82 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java
@@ -9,6 +9,7 @@
import org.springframework.web.bind.annotation.*;
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.service.CustomPDDocumentFactory;
@@ -21,6 +22,7 @@
*/
@RestController
@RequestMapping("/api/v1/general")
+@Tag(name = "General", description = "General APIs")
public class PdfImageRemovalController {
// Service for removing images from PDFs
diff --git a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java
index 9b7714ad2df..19e135021b2 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java
@@ -109,7 +109,7 @@ public ResponseEntity scalePages(@ModelAttribute ScalePagesRequest reque
}
private PDRectangle getTargetSize(String targetPDRectangle, PDDocument sourceDocument) {
- if (targetPDRectangle.equals("KEEP")) {
+ if ("KEEP".equals(targetPDRectangle)) {
if (sourceDocument.getNumberOfPages() == 0) {
return null;
}
diff --git a/src/main/java/stirling/software/SPDF/controller/api/SettingsController.java b/src/main/java/stirling/software/SPDF/controller/api/SettingsController.java
new file mode 100644
index 00000000000..dd6742108ad
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/controller/api/SettingsController.java
@@ -0,0 +1,37 @@
+package stirling.software.SPDF.controller.api;
+
+import java.io.IOException;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import io.swagger.v3.oas.annotations.Hidden;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import stirling.software.SPDF.model.ApplicationProperties;
+import stirling.software.SPDF.utils.GeneralUtils;
+
+@Controller
+@Tag(name = "Settings", description = "Settings APIs")
+@RequestMapping("/api/v1/settings")
+@Hidden
+public class SettingsController {
+
+ @Autowired ApplicationProperties applicationProperties;
+
+ @PostMapping("/update-enable-analytics")
+ @Hidden
+ public ResponseEntity updateApiKey(@RequestBody Boolean enabled) throws IOException {
+ if (!"undefined".equals(applicationProperties.getSystem().getEnableAnalytics())) {
+ return ResponseEntity.status(HttpStatus.ALREADY_REPORTED)
+ .body(
+ "Setting has already been set, To adjust please edit /config/settings.yml");
+ }
+ GeneralUtils.saveKeyToConfig("system.enableAnalytics", String.valueOf(enabled), false);
+ applicationProperties.getSystem().setEnableAnalytics(String.valueOf(enabled));
+ return ResponseEntity.ok("Updated");
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java
index 2da76fa363e..e27df103374 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java
@@ -60,8 +60,6 @@ public ResponseEntity splitPdf(@ModelAttribute PDFWithPageNums request)
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
int totalPages = document.getNumberOfPages();
List pageNumbers = request.getPageNumbersList(document, false);
- System.out.println(
- pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
if (!pageNumbers.contains(totalPages - 1)) {
// Create a mutable ArrayList so we can add to it
pageNumbers = new ArrayList<>(pageNumbers);
diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java
index 0cca8f7886a..f344c2763e0 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java
@@ -32,9 +32,9 @@
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
-import stirling.software.SPDF.config.PdfMetadataService;
import stirling.software.SPDF.model.PdfMetadata;
import stirling.software.SPDF.model.api.SplitPdfByChaptersRequest;
+import stirling.software.SPDF.service.PdfMetadataService;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
diff --git a/src/main/java/stirling/software/SPDF/controller/api/UserController.java b/src/main/java/stirling/software/SPDF/controller/api/UserController.java
index 719adac19a5..d27534875b1 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/UserController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/UserController.java
@@ -30,7 +30,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.UserService;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.Role;
@@ -40,6 +42,7 @@
@Controller
@Tag(name = "User", description = "User APIs")
@RequestMapping("/api/v1/user")
+@Slf4j
public class UserController {
@Autowired private UserService userService;
@@ -191,13 +194,11 @@ public String updateUserSettings(HttpServletRequest request, Principal principal
Map paramMap = request.getParameterMap();
Map updates = new HashMap<>();
- System.out.println("Received parameter map: " + paramMap);
-
for (Map.Entry entry : paramMap.entrySet()) {
updates.put(entry.getKey(), entry.getValue()[0]);
}
- System.out.println("Processed updates: " + updates);
+ log.debug("Processed updates: " + updates);
// Assuming you have a method in userService to update the settings for a user
userService.updateUserSettings(principal.getName(), updates);
@@ -209,7 +210,7 @@ public String updateUserSettings(HttpServletRequest request, Principal principal
@PostMapping("/admin/saveUser")
public RedirectView saveUser(
@RequestParam(name = "username", required = true) String username,
- @RequestParam(name = "password", required = true) String password,
+ @RequestParam(name = "password", required = false) String password,
@RequestParam(name = "role") String role,
@RequestParam(name = "authType") String authType,
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
@@ -336,6 +337,8 @@ public RedirectView changeUserEnabled(
userNameP = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
userNameP = ((OAuth2User) principal).getName();
+ } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
+ userNameP = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
} else if (principal instanceof String) {
userNameP = (String) principal;
}
diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java
index 694d30ab881..778aab0473c 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java
@@ -1,5 +1,6 @@
package stirling.software.SPDF.controller.api.converters;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -14,7 +15,6 @@
import stirling.software.SPDF.utils.FileToPdf;
import stirling.software.SPDF.utils.WebResponseUtils;
-// Disabled for now
// @RestController
// @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
@@ -24,7 +24,7 @@ public class ConvertBookToPDFController {
private final CustomPDDocumentFactory pdfDocumentFactory;
- // @Autowired
+ @Autowired
public ConvertBookToPDFController(
CustomPDDocumentFactory pdfDocumentFactory,
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
@@ -66,6 +66,8 @@ public ResponseEntity HtmlToPdf(@ModelAttribute GeneralFile request) thr
}
byte[] pdfBytes = FileToPdf.convertBookTypeToPdf(fileInput.getBytes(), originalFilename);
+ pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
+
String outputFilename =
originalFilename.replaceFirst("[.][^.]+$", "")
+ ".pdf"; // Remove file extension and append .pdf
diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java
index 19ba1ac43aa..a4fe57bb99c 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java
@@ -1,27 +1,39 @@
package stirling.software.SPDF.controller.api.converters;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest;
+import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.FileToPdf;
import stirling.software.SPDF.utils.WebResponseUtils;
-// Disabled for now
-// @RestController
-// @Tag(name = "Convert", description = "Convert APIs")
-// @RequestMapping("/api/v1/convert")
+@RestController
+@Tag(name = "Convert", description = "Convert APIs")
+@RequestMapping("/api/v1/convert")
public class ConvertHtmlToPDF {
- // @Autowired
- @Qualifier("bookAndHtmlFormatsInstalled")
- private boolean bookAndHtmlFormatsInstalled;
+ private final boolean bookAndHtmlFormatsInstalled;
+
+ private final CustomPDDocumentFactory pdfDocumentFactory;
+
+ @Autowired
+ public ConvertHtmlToPDF(
+ CustomPDDocumentFactory pdfDocumentFactory,
+ @Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
+ this.pdfDocumentFactory = pdfDocumentFactory;
+ this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
+ }
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf")
@Operation(
@@ -49,6 +61,8 @@ public ResponseEntity HtmlToPdf(@ModelAttribute HTMLToPdfRequest request
originalFilename,
bookAndHtmlFormatsInstalled);
+ pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
+
String outputFilename =
originalFilename.replaceFirst("[.][^.]+$", "")
+ ".pdf"; // Remove file extension and append .pdf
diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java
index 8cf4246ecd8..be955dbd386 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java
@@ -82,7 +82,7 @@ public ResponseEntity convertToImage(@ModelAttribute ConvertToImageReque
result =
PdfUtils.convertFromPdf(
pdfBytes,
- imageFormat.equalsIgnoreCase("webp") ? "png" : imageFormat.toUpperCase(),
+ "webp".equalsIgnoreCase(imageFormat) ? "png" : imageFormat.toUpperCase(),
colorTypeResult,
singleImage,
Integer.valueOf(dpi),
@@ -90,9 +90,9 @@ public ResponseEntity convertToImage(@ModelAttribute ConvertToImageReque
if (result == null || result.length == 0) {
logger.error("resultant bytes for {} is null, error converting ", filename);
}
- if (imageFormat.equalsIgnoreCase("webp") && !CheckProgramInstall.isPythonAvailable()) {
+ if ("webp".equalsIgnoreCase(imageFormat) && !CheckProgramInstall.isPythonAvailable()) {
throw new IOException("Python is not installed. Required for WebP conversion.");
- } else if (imageFormat.equalsIgnoreCase("webp")
+ } else if ("webp".equalsIgnoreCase(imageFormat)
&& CheckProgramInstall.isPythonAvailable()) {
// Write the output stream to a temp file
Path tempFile = Files.createTempFile("temp_png", ".png");
diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java
index 5b2f3fdf5b1..b378f479fdf 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java
@@ -10,28 +10,40 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.renderer.html.HtmlRenderer;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.GeneralFile;
+import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.FileToPdf;
import stirling.software.SPDF.utils.WebResponseUtils;
-// Disabled for now
-// @RestController
-// @Tag(name = "Convert", description = "Convert APIs")
-// @RequestMapping("/api/v1/convert")
+@RestController
+@Tag(name = "Convert", description = "Convert APIs")
+@RequestMapping("/api/v1/convert")
public class ConvertMarkdownToPdf {
- // @Autowired
- @Qualifier("bookAndHtmlFormatsInstalled")
- private boolean bookAndHtmlFormatsInstalled;
+ private final boolean bookAndHtmlFormatsInstalled;
+
+ private final CustomPDDocumentFactory pdfDocumentFactory;
+
+ @Autowired
+ public ConvertMarkdownToPdf(
+ CustomPDDocumentFactory pdfDocumentFactory,
+ @Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
+ this.pdfDocumentFactory = pdfDocumentFactory;
+ this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
+ }
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf")
@Operation(
@@ -70,7 +82,7 @@ public ResponseEntity markdownToPdf(@ModelAttribute GeneralFile request)
htmlContent.getBytes(),
"converted.html",
bookAndHtmlFormatsInstalled);
-
+ pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
String outputFilename =
originalFilename.replaceFirst("[.][^.]+$", "")
+ ".pdf"; // Remove file extension and append .pdf
diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java
index 181b5713ad8..f1c672e12be 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java
@@ -6,6 +6,7 @@
import java.util.Arrays;
import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -20,13 +21,12 @@
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
-// Disabled for now
// @RestController
// @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
public class ConvertPDFToBookController {
- // @Autowired
+ @Autowired
@Qualifier("bookAndHtmlFormatsInstalled")
private boolean bookAndHtmlFormatsInstalled;
diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java
index 14aef73462f..c437c4cb0a0 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java
@@ -61,7 +61,7 @@ public ResponseEntity pdfToPdfA(@ModelAttribute PdfToPdfARequest request
command.add("-dPDFA=" + ("pdfa".equals(outputFormat) ? "2" : "1"));
command.add("-dNOPAUSE");
command.add("-dBATCH");
- command.add("-sColorConversionStrategy=UseDeviceIndependentColor");
+ command.add("-sColorConversionStrategy=sRGB");
command.add("-sDEVICE=pdfwrite");
command.add("-dPDFACompatibilityPolicy=2");
command.add("-o");
diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java
index 3dff43e8492..075de0d143e 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java
@@ -60,8 +60,6 @@ public ResponseEntity extractImages(@ModelAttribute PDFExtractImagesRequ
MultipartFile file = request.getFileInput();
String format = request.getFormat();
boolean allowDuplicates = request.isAllowDuplicates();
- System.out.println(
- System.currentTimeMillis() + " file=" + file.getName() + ", format=" + format);
PDDocument document = Loader.loadPDF(file.getBytes());
// Determine if multithreading should be used based on PDF size or number of pages
diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/PrintFileController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/PrintFileController.java
index bc0a6715f82..8f993abd054 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/misc/PrintFileController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/misc/PrintFileController.java
@@ -26,11 +26,13 @@
import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.PrintFileRequest;
@RestController
@RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs")
+@Slf4j
public class PrintFileController {
// TODO
@@ -59,7 +61,7 @@ public ResponseEntity printFile(@ModelAttribute PrintFileRequest request
new IllegalArgumentException(
"No matching printer found"));
- System.out.println("Selected Printer: " + selectedService.getName());
+ log.info("Selected Printer: " + selectedService.getName());
if ("application/pdf".equals(contentType)) {
PDDocument document = Loader.loadPDF(file.getBytes());
diff --git a/src/main/java/stirling/software/SPDF/controller/api/pipeline/UserServiceInterface.java b/src/main/java/stirling/software/SPDF/controller/api/pipeline/UserServiceInterface.java
index c3facb35e11..4ef6e6ec44e 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/pipeline/UserServiceInterface.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/pipeline/UserServiceInterface.java
@@ -4,4 +4,6 @@ public interface UserServiceInterface {
String getApiKeyForUser(String username);
String getCurrentUsername();
+
+ long getTotalUsersCount();
}
diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java
index 40dc2c75de3..d9e1e4ca976 100644
--- a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java
+++ b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java
@@ -58,7 +58,6 @@ public ResponseEntity redactPdf(@ModelAttribute RedactPdfRequest request
float customPadding = request.getCustomPadding();
boolean convertPDFToImage = request.isConvertPDFToImage();
- System.out.println(listOfTextString);
String[] listOfText = listOfTextString.split("\n");
PDDocument document = pdfDocumentFactory.load(file);
@@ -75,7 +74,6 @@ public ResponseEntity redactPdf(@ModelAttribute RedactPdfRequest request
for (String text : listOfText) {
text = text.trim();
- System.out.println(text);
TextFinder textFinder = new TextFinder(text, useRegex, wholeWordSearchBool);
List foundTexts = textFinder.getTextLocations(document);
redactFoundText(document, foundTexts, customPadding, redactColor);
diff --git a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java
index 9081164d285..8dcb2a9a5fb 100644
--- a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java
+++ b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java
@@ -21,10 +21,13 @@
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
+import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.*;
+import stirling.software.SPDF.model.ApplicationProperties.Security;
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.provider.GithubProvider;
import stirling.software.SPDF.model.provider.GoogleProvider;
import stirling.software.SPDF.model.provider.KeycloakProvider;
@@ -51,38 +54,54 @@ public String login(HttpServletRequest request, Model model, Authentication auth
Map providerList = new HashMap<>();
- OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
+ Security securityProps = applicationProperties.getSecurity();
+
+ OAUTH2 oauth = securityProps.getOauth2();
if (oauth != null) {
- if (oauth.isSettingsValid()) {
- providerList.put("oidc", oauth.getProvider());
- }
- Client client = oauth.getClient();
- if (client != null) {
- GoogleProvider google = client.getGoogle();
- if (google.isSettingsValid()) {
- providerList.put(google.getName(), google.getClientName());
+ if (oauth.getEnabled()) {
+ if (oauth.isSettingsValid()) {
+ providerList.put("/oauth2/authorization/oidc", oauth.getProvider());
}
+ Client client = oauth.getClient();
+ if (client != null) {
+ GoogleProvider google = client.getGoogle();
+ if (google.isSettingsValid()) {
+ providerList.put(
+ "/oauth2/authorization/" + google.getName(),
+ google.getClientName());
+ }
- GithubProvider github = client.getGithub();
- if (github.isSettingsValid()) {
- providerList.put(github.getName(), github.getClientName());
- }
+ GithubProvider github = client.getGithub();
+ if (github.isSettingsValid()) {
+ providerList.put(
+ "/oauth2/authorization/" + github.getName(),
+ github.getClientName());
+ }
- KeycloakProvider keycloak = client.getKeycloak();
- if (keycloak.isSettingsValid()) {
- providerList.put(keycloak.getName(), keycloak.getClientName());
+ KeycloakProvider keycloak = client.getKeycloak();
+ if (keycloak.isSettingsValid()) {
+ providerList.put(
+ "/oauth2/authorization/" + keycloak.getName(),
+ keycloak.getClientName());
+ }
}
}
}
+
+ SAML2 saml2 = securityProps.getSaml2();
+ if (saml2 != null) {
+ if (saml2.getEnabled()) {
+ providerList.put("/saml2/authenticate/" + saml2.getRegistrationId(), "SAML 2");
+ }
+ }
// Remove any null keys/values from the providerList
providerList
.entrySet()
.removeIf(entry -> entry.getKey() == null || entry.getValue() == null);
model.addAttribute("providerlist", providerList);
- model.addAttribute("loginMethod", applicationProperties.getSecurity().getLoginMethod());
- model.addAttribute(
- "oAuth2Enabled", applicationProperties.getSecurity().getOauth2().getEnabled());
+ model.addAttribute("loginMethod", securityProps.getLoginMethod());
+ model.addAttribute("altLogin", securityProps.isAltLogin());
model.addAttribute("currentPage", "login");
@@ -349,6 +368,17 @@ public String account(HttpServletRequest request, Model model, Authentication au
// Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", true);
}
+ if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
+ // Cast the principal object to OAuth2User
+ CustomSaml2AuthenticatedPrincipal userDetails =
+ (CustomSaml2AuthenticatedPrincipal) principal;
+
+ // Retrieve username and other attributes
+ username = userDetails.getName();
+ // Add oAuth2 Login attributes to the model
+ model.addAttribute("oAuth2Login", true);
+ }
+
if (username != null) {
// Fetch user details from the database
Optional user =
diff --git a/src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java b/src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java
index 93ad5f342e0..14af6ee7a5a 100644
--- a/src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java
+++ b/src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java
@@ -108,6 +108,13 @@ public String splitPdfBySections(Model model) {
return "split-pdf-by-sections";
}
+ @GetMapping("/split-pdf-by-chapters")
+ @Hidden
+ public String splitPdfByChapters(Model model) {
+ model.addAttribute("currentPage", "split-pdf-by-chapters");
+ return "split-pdf-by-chapters";
+ }
+
@GetMapping("/view-pdf")
@Hidden
public String ViewPdfForm2(Model model) {
diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java
index 46c92633a48..16397965ad7 100644
--- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java
+++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java
@@ -1,18 +1,29 @@
package stirling.software.SPDF.model;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
import lombok.ToString;
import stirling.software.SPDF.config.YamlPropertySourceFactory;
import stirling.software.SPDF.model.provider.GithubProvider;
@@ -24,6 +35,7 @@
@ConfigurationProperties(prefix = "")
@PropertySource(value = "file:./configs/settings.yml", factory = YamlPropertySourceFactory.class)
@Data
+@Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationProperties {
private Legal legal = new Legal();
@@ -35,7 +47,6 @@ public class ApplicationProperties {
private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
private AutoPipeline autoPipeline = new AutoPipeline();
- private static final Logger logger = LoggerFactory.getLogger(ApplicationProperties.class);
@Data
public static class AutoPipeline {
@@ -57,16 +68,112 @@ public static class Security {
private Boolean csrfDisabled;
private InitialLogin initialLogin = new InitialLogin();
private OAUTH2 oauth2 = new OAUTH2();
+ private SAML2 saml2 = new SAML2();
private int loginAttemptCount;
private long loginResetTimeMinutes;
private String loginMethod = "all";
+ public Boolean isAltLogin() {
+ return saml2.getEnabled() || oauth2.getEnabled();
+ }
+
+ public enum LoginMethods {
+ ALL("all"),
+ NORMAL("normal"),
+ OAUTH2("oauth2"),
+ SAML2("saml2");
+
+ private String method;
+
+ LoginMethods(String method) {
+ this.method = method;
+ }
+
+ @Override
+ public String toString() {
+ return method;
+ }
+ }
+
+ public boolean isUserPass() {
+ return (loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString())
+ || loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString()));
+ }
+
+ public boolean isOauth2Activ() {
+ return (oauth2 != null
+ && oauth2.getEnabled()
+ && !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
+ }
+
+ public boolean isSaml2Activ() {
+ return (saml2 != null
+ && saml2.getEnabled()
+ && !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
+ }
+
@Data
public static class InitialLogin {
private String username;
@ToString.Exclude private String password;
}
+ @Getter
+ @Setter
+ public static class SAML2 {
+ private Boolean enabled = false;
+ private Boolean autoCreateUser = false;
+ private Boolean blockRegistration = false;
+ private String registrationId = "stirling";
+ private String idpMetadataUri;
+ private String idpSingleLogoutUrl;
+ private String idpSingleLoginUrl;
+ private String idpIssuer;
+ private String idpCert;
+ private String privateKey;
+ private String spCert;
+
+ public InputStream getIdpMetadataUri() throws IOException {
+ if (idpMetadataUri.startsWith("classpath:")) {
+ return new ClassPathResource(idpMetadataUri.substring("classpath".length()))
+ .getInputStream();
+ }
+ try {
+ URI uri = new URI(idpMetadataUri);
+ URL url = uri.toURL();
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ return connection.getInputStream();
+ } catch (URISyntaxException e) {
+ throw new IOException("Invalid URI format: " + idpMetadataUri, e);
+ }
+ }
+
+ public Resource getSpCert() {
+ if (spCert.startsWith("classpath:")) {
+ return new ClassPathResource(spCert.substring("classpath:".length()));
+ } else {
+ return new FileSystemResource(spCert);
+ }
+ }
+
+ public Resource getidpCert() {
+ if (idpCert.startsWith("classpath:")) {
+ return new ClassPathResource(idpCert.substring("classpath:".length()));
+ } else {
+ return new FileSystemResource(idpCert);
+ }
+ }
+
+ public Resource getPrivateKey() {
+ if (privateKey.startsWith("classpath:")) {
+ return new ClassPathResource(privateKey.substring("classpath:".length()));
+ } else {
+ return new FileSystemResource(privateKey);
+ }
+ }
+ }
+
@Data
public static class OAUTH2 {
private Boolean enabled = false;
@@ -136,6 +243,7 @@ public static class System {
private boolean customHTMLFiles;
private String tessdataDir;
private Boolean enableAlphaFunctionality;
+ private String enableAnalytics;
}
@Data
@@ -175,11 +283,14 @@ public static class Metrics {
@Data
public static class AutomaticallyGenerated {
@ToString.Exclude private String key;
+ private String UUID;
}
@Data
public static class EnterpriseEdition {
+ private boolean enabled;
@ToString.Exclude private String key;
+ private int maxUsers;
private CustomMetadata customMetadata = new CustomMetadata();
@Data
diff --git a/src/main/java/stirling/software/SPDF/model/Provider.java b/src/main/java/stirling/software/SPDF/model/Provider.java
index c378975d393..87f5fa29847 100644
--- a/src/main/java/stirling/software/SPDF/model/Provider.java
+++ b/src/main/java/stirling/software/SPDF/model/Provider.java
@@ -19,7 +19,6 @@ protected boolean isValid(String value, String name) {
return true;
}
return false;
- // throw new IllegalArgumentException(getName() + ": " + name + " is required!");
}
protected boolean isValid(Collection value, String name) {
@@ -27,66 +26,55 @@ protected boolean isValid(Collection value, String name) {
return true;
}
return false;
- // throw new IllegalArgumentException(getName() + ": " + name + " is required!");
}
@Override
public Collection getScopes() {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getScope'");
}
@Override
public void setScopes(String scopes) {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setScope'");
}
@Override
public String getUseAsUsername() {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getUseAsUsername'");
}
@Override
public void setUseAsUsername(String useAsUsername) {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setUseAsUsername'");
}
@Override
public String getIssuer() {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getIssuer'");
}
@Override
public void setIssuer(String issuer) {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setIssuer'");
}
@Override
public String getClientSecret() {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getClientSecret'");
}
@Override
public void setClientSecret(String clientSecret) {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setClientSecret'");
}
@Override
public String getClientId() {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getClientId'");
}
@Override
public void setClientId(String clientId) {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setClientId'");
}
}
diff --git a/src/main/java/stirling/software/SPDF/pdf/TextFinder.java b/src/main/java/stirling/software/SPDF/pdf/TextFinder.java
index f9e339c2fa4..77f7d0c6f47 100644
--- a/src/main/java/stirling/software/SPDF/pdf/TextFinder.java
+++ b/src/main/java/stirling/software/SPDF/pdf/TextFinder.java
@@ -10,8 +10,10 @@
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
+import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.PDFText;
+@Slf4j
public class TextFinder extends PDFTextStripper {
private final String searchText;
@@ -92,7 +94,7 @@ protected void writeString(String text, List textPositions) {
public List getTextLocations(PDDocument document) throws Exception {
this.getText(document);
- System.out.println(
+ log.debug(
"Found "
+ textOccurrences.size()
+ " occurrences of '"
diff --git a/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java b/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java
index 2d3d8990e0b..0807e0d110e 100644
--- a/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java
+++ b/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java
@@ -13,7 +13,6 @@
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
-import stirling.software.SPDF.config.PdfMetadataService;
import stirling.software.SPDF.model.PdfMetadata;
import stirling.software.SPDF.model.api.PDFFile;
@@ -35,6 +34,36 @@ public PDDocument createNewDocument() throws IOException {
return document;
}
+ public byte[] createNewBytesBasedOnOldDocument(byte[] oldDocument) throws IOException {
+ PDDocument document = Loader.loadPDF(oldDocument);
+ return createNewBytesBasedOnOldDocument(document);
+ }
+
+ public byte[] createNewBytesBasedOnOldDocument(File oldDocument) throws IOException {
+ PDDocument document = Loader.loadPDF(oldDocument);
+ return createNewBytesBasedOnOldDocument(document);
+ }
+
+ public byte[] createNewBytesBasedOnOldDocument(PDDocument oldDocument) throws IOException {
+ pdfMetadataService.setMetadataToPdf(
+ oldDocument, pdfMetadataService.extractMetadataFromPdf(oldDocument), true);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ oldDocument.save(baos);
+ oldDocument.close();
+ return baos.toByteArray();
+ }
+
+ public PDDocument createNewDocumentBasedOnOldDocument(byte[] oldDocument) throws IOException {
+ PDDocument document = Loader.loadPDF(oldDocument);
+ return createNewDocumentBasedOnOldDocument(document);
+ }
+
+ public PDDocument createNewDocumentBasedOnOldDocument(File oldDocument) throws IOException {
+ PDDocument document = Loader.loadPDF(oldDocument);
+ return createNewDocumentBasedOnOldDocument(document);
+ }
+
public PDDocument createNewDocumentBasedOnOldDocument(PDDocument oldDocument)
throws IOException {
PDDocument document = new PDDocument();
diff --git a/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java b/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java
new file mode 100644
index 00000000000..eb3a83b0165
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java
@@ -0,0 +1,56 @@
+package stirling.software.SPDF.service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.search.Search;
+
+@Service
+public class MetricsAggregatorService {
+
+ private final MeterRegistry meterRegistry;
+ private final PostHogService postHogService;
+ private final Map lastSentMetrics = new ConcurrentHashMap<>();
+
+ @Autowired
+ public MetricsAggregatorService(MeterRegistry meterRegistry, PostHogService postHogService) {
+ this.meterRegistry = meterRegistry;
+ this.postHogService = postHogService;
+ }
+
+ @Scheduled(fixedRate = 900000) // Run every 15 minutes
+ public void aggregateAndSendMetrics() {
+ Map metrics = new HashMap<>();
+ Search.in(meterRegistry)
+ .name("http.requests")
+ .counters()
+ .forEach(
+ counter -> {
+ String key =
+ String.format(
+ "http_requests_%s_%s",
+ counter.getId().getTag("method"),
+ counter.getId().getTag("uri").replace("/", "_"));
+
+ double currentCount = counter.count();
+ double lastCount = lastSentMetrics.getOrDefault(key, 0.0);
+ double difference = currentCount - lastCount;
+
+ if (difference > 0) {
+ metrics.put(key, difference);
+ lastSentMetrics.put(key, currentCount);
+ }
+ });
+
+ // Send aggregated metrics to PostHog
+ if (!metrics.isEmpty()) {
+ postHogService.captureEvent("aggregated_metrics", metrics);
+ }
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/config/PdfMetadataService.java b/src/main/java/stirling/software/SPDF/service/PdfMetadataService.java
similarity index 62%
rename from src/main/java/stirling/software/SPDF/config/PdfMetadataService.java
rename to src/main/java/stirling/software/SPDF/service/PdfMetadataService.java
index 7b40a878aed..373b4c916ff 100644
--- a/src/main/java/stirling/software/SPDF/config/PdfMetadataService.java
+++ b/src/main/java/stirling/software/SPDF/service/PdfMetadataService.java
@@ -1,4 +1,4 @@
-package stirling.software.SPDF.config;
+package stirling.software.SPDF.service;
import java.util.Calendar;
@@ -15,16 +15,16 @@
public class PdfMetadataService {
private final ApplicationProperties applicationProperties;
- private final String appVersion;
+ private final String stirlingPDFLabel;
private final UserServiceInterface userService;
@Autowired
public PdfMetadataService(
ApplicationProperties applicationProperties,
- @Qualifier("appVersion") String appVersion,
+ @Qualifier("StirlingPDFLabel") String stirlingPDFLabel,
@Autowired(required = false) UserServiceInterface userService) {
this.applicationProperties = applicationProperties;
- this.appVersion = appVersion;
+ this.stirlingPDFLabel = stirlingPDFLabel;
this.userService = userService;
}
@@ -59,51 +59,40 @@ public void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata, boolean ne
private void setNewDocumentMetadata(PDDocument pdf, PdfMetadata pdfMetadata) {
- String creator = "Stirling-PDF";
+ String creator = stirlingPDFLabel;
- // if (applicationProperties
- // .getEnterpriseEdition()
- // .getCustomMetadata()
- // .isAutoUpdateMetadata()) {
+ if (applicationProperties
+ .getEnterpriseEdition()
+ .getCustomMetadata()
+ .isAutoUpdateMetadata()) {
- // producer =
- //
- // applicationProperties.getEnterpriseEdition().getCustomMetadata().getProducer();
- // creator =
- // applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator();
- // title = applicationProperties.getEnterpriseEdition().getCustomMetadata().getTitle();
-
- // if ("{filename}".equals(title)) {
- // title = "Filename"; // Replace with actual filename logic
- // } else if ("{unchanged}".equals(title)) {
- // title = pdfMetadata.getTitle(); // Keep the original title
- // }
- // }
+ creator = applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator();
+ pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
+ }
- pdf.getDocumentInformation().setCreator(creator + " " + appVersion);
+ pdf.getDocumentInformation().setCreator(creator);
pdf.getDocumentInformation().setCreationDate(Calendar.getInstance());
}
private void setCommonMetadata(PDDocument pdf, PdfMetadata pdfMetadata) {
- String producer = "Stirling-PDF";
String title = pdfMetadata.getTitle();
pdf.getDocumentInformation().setTitle(title);
- pdf.getDocumentInformation().setProducer(producer + " " + appVersion);
+ pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject());
pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords());
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
String author = pdfMetadata.getAuthor();
- // if (applicationProperties
- // .getEnterpriseEdition()
- // .getCustomMetadata()
- // .isAutoUpdateMetadata()) {
- // author = applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor();
-
- // if (userService != null) {
- // author = author.replace("username", userService.getCurrentUsername());
- // }
- // }
+ if (applicationProperties
+ .getEnterpriseEdition()
+ .getCustomMetadata()
+ .isAutoUpdateMetadata()) {
+ author = applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor();
+
+ if (userService != null) {
+ author = author.replace("username", userService.getCurrentUsername());
+ }
+ }
pdf.getDocumentInformation().setAuthor(author);
}
}
diff --git a/src/main/java/stirling/software/SPDF/service/PostHogService.java b/src/main/java/stirling/software/SPDF/service/PostHogService.java
new file mode 100644
index 00000000000..381bb0147ba
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/service/PostHogService.java
@@ -0,0 +1,388 @@
+package stirling.software.SPDF.service;
+
+import java.io.File;
+import java.lang.management.*;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import com.posthog.java.PostHog;
+
+import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
+import stirling.software.SPDF.model.ApplicationProperties;
+
+@Service
+public class PostHogService {
+ private final PostHog postHog;
+ private final String uniqueId;
+ private final ApplicationProperties applicationProperties;
+ private final UserServiceInterface userService;
+
+
+ @Autowired
+ public PostHogService(
+ PostHog postHog,
+ @Qualifier("UUID") String uuid,
+ ApplicationProperties applicationProperties, @Autowired(required = false) UserServiceInterface userService) {
+ this.postHog = postHog;
+ this.uniqueId = uuid;
+ this.applicationProperties = applicationProperties;
+ this.userService = userService;
+ captureSystemInfo();
+ }
+
+ private void captureSystemInfo() {
+ if (!Boolean.getBoolean(applicationProperties.getSystem().getEnableAnalytics())) {
+ return;
+ }
+ try {
+ postHog.capture(uniqueId, "system_info_captured", captureServerMetrics());
+ } catch (Exception e) {
+ // Handle exceptions
+ }
+ }
+
+ public void captureEvent(String eventName, Map properties) {
+ if (!Boolean.getBoolean(applicationProperties.getSystem().getEnableAnalytics())) {
+ return;
+ }
+ postHog.capture(uniqueId, eventName, properties);
+ }
+
+ public Map captureServerMetrics() {
+ Map metrics = new HashMap<>();
+
+ try {
+ // System info
+ metrics.put("os_name", System.getProperty("os.name"));
+ metrics.put("os_version", System.getProperty("os.version"));
+ metrics.put("java_version", System.getProperty("java.version"));
+ metrics.put("user_name", System.getProperty("user.name"));
+ metrics.put("user_home", System.getProperty("user.home"));
+ metrics.put("user_dir", System.getProperty("user.dir"));
+
+ // CPU and Memory
+ metrics.put("cpu_cores", Runtime.getRuntime().availableProcessors());
+ metrics.put("total_memory", Runtime.getRuntime().totalMemory());
+ metrics.put("free_memory", Runtime.getRuntime().freeMemory());
+
+ // Network and Server Identity
+ InetAddress localHost = InetAddress.getLocalHost();
+ metrics.put("ip_address", localHost.getHostAddress());
+ metrics.put("hostname", localHost.getHostName());
+ metrics.put("mac_address", getMacAddress());
+
+ // JVM info
+ metrics.put("jvm_vendor", System.getProperty("java.vendor"));
+ metrics.put("jvm_version", System.getProperty("java.vm.version"));
+
+ // Locale and Timezone
+ metrics.put("system_language", System.getProperty("user.language"));
+ metrics.put("system_country", System.getProperty("user.country"));
+ metrics.put("timezone", TimeZone.getDefault().getID());
+ metrics.put("locale", Locale.getDefault().toString());
+
+ // Disk info
+ File root = new File(".");
+ metrics.put("total_disk_space", root.getTotalSpace());
+ metrics.put("free_disk_space", root.getFreeSpace());
+
+ // Process info
+ metrics.put("process_id", ProcessHandle.current().pid());
+
+ // JVM metrics
+ RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+ metrics.put("jvm_uptime_ms", runtimeMXBean.getUptime());
+ metrics.put("jvm_start_time", runtimeMXBean.getStartTime());
+
+ // Memory metrics
+ MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
+ metrics.put("heap_memory_usage", memoryMXBean.getHeapMemoryUsage().getUsed());
+ metrics.put("non_heap_memory_usage", memoryMXBean.getNonHeapMemoryUsage().getUsed());
+
+ // CPU metrics
+ OperatingSystemMXBean osMXBean = ManagementFactory.getOperatingSystemMXBean();
+ metrics.put("system_load_average", osMXBean.getSystemLoadAverage());
+
+ // Thread metrics
+ ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+ metrics.put("thread_count", threadMXBean.getThreadCount());
+ metrics.put("daemon_thread_count", threadMXBean.getDaemonThreadCount());
+ metrics.put("peak_thread_count", threadMXBean.getPeakThreadCount());
+
+ // Garbage collection metrics
+ for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
+ metrics.put("gc_" + gcBean.getName() + "_count", gcBean.getCollectionCount());
+ metrics.put("gc_" + gcBean.getName() + "_time", gcBean.getCollectionTime());
+ }
+
+ // Network interfaces
+ metrics.put("network_interfaces", getNetworkInterfacesInfo());
+
+ // Docker detection and stats
+ boolean isDocker = isRunningInDocker();
+ metrics.put("is_docker", isDocker);
+ if (isDocker) {
+ metrics.put("docker_metrics", getDockerMetrics());
+ }
+ metrics.put("application_properties", captureApplicationProperties());
+
+
+ if(userService != null) {
+ metrics.put("total_users_created", userService.getTotalUsersCount());
+ }
+
+ } catch (Exception e) {
+ metrics.put("error", e.getMessage());
+ }
+
+ return metrics;
+ }
+
+ private boolean isRunningInDocker() {
+ return Files.exists(Paths.get("/.dockerenv"));
+ }
+
+ private Map getDockerMetrics() {
+ Map dockerMetrics = new HashMap<>();
+
+ // Network-related Docker info
+ dockerMetrics.put("docker_network_mode", System.getenv("DOCKER_NETWORK_MODE"));
+
+ // Container name (if set)
+ String containerName = System.getenv("CONTAINER_NAME");
+ if (containerName != null && !containerName.isEmpty()) {
+ dockerMetrics.put("container_name", containerName);
+ }
+
+ // Docker compose information
+ String composeProject = System.getenv("COMPOSE_PROJECT_NAME");
+ String composeService = System.getenv("COMPOSE_SERVICE_NAME");
+ if (composeProject != null && composeService != null) {
+ dockerMetrics.put("compose_project", composeProject);
+ dockerMetrics.put("compose_service", composeService);
+ }
+
+ // Kubernetes-specific info (if running in K8s)
+ String k8sPodName = System.getenv("KUBERNETES_POD_NAME");
+ if (k8sPodName != null) {
+ dockerMetrics.put("k8s_pod_name", k8sPodName);
+ dockerMetrics.put("k8s_namespace", System.getenv("KUBERNETES_NAMESPACE"));
+ dockerMetrics.put("k8s_node_name", System.getenv("KUBERNETES_NODE_NAME"));
+ }
+
+ // New environment variables
+ dockerMetrics.put("version_tag", System.getenv("VERSION_TAG"));
+ dockerMetrics.put("docker_enable_security", System.getenv("DOCKER_ENABLE_SECURITY"));
+ dockerMetrics.put("fat_docker", System.getenv("FAT_DOCKER"));
+
+ return dockerMetrics;
+ }
+
+ private void addIfNotEmpty(Map map, String key, Object value) {
+ if (value != null) {
+ if (value instanceof String) {
+ String strValue = (String) value;
+ if (!StringUtils.isBlank(strValue)) {
+ map.put(key, strValue.trim());
+ }
+ } else {
+ map.put(key, value);
+ }
+ }
+ }
+
+ public Map captureApplicationProperties() {
+ Map properties = new HashMap<>();
+
+ // Capture Legal properties
+ addIfNotEmpty(
+ properties,
+ "legal_termsAndConditions",
+ applicationProperties.getLegal().getTermsAndConditions());
+ addIfNotEmpty(
+ properties,
+ "legal_privacyPolicy",
+ applicationProperties.getLegal().getPrivacyPolicy());
+ addIfNotEmpty(
+ properties,
+ "legal_accessibilityStatement",
+ applicationProperties.getLegal().getAccessibilityStatement());
+ addIfNotEmpty(
+ properties,
+ "legal_cookiePolicy",
+ applicationProperties.getLegal().getCookiePolicy());
+ addIfNotEmpty(
+ properties, "legal_impressum", applicationProperties.getLegal().getImpressum());
+
+ // Capture Security properties
+ addIfNotEmpty(
+ properties,
+ "security_enableLogin",
+ applicationProperties.getSecurity().getEnableLogin());
+ addIfNotEmpty(
+ properties,
+ "security_csrfDisabled",
+ applicationProperties.getSecurity().getCsrfDisabled());
+ addIfNotEmpty(
+ properties,
+ "security_loginAttemptCount",
+ applicationProperties.getSecurity().getLoginAttemptCount());
+ addIfNotEmpty(
+ properties,
+ "security_loginResetTimeMinutes",
+ applicationProperties.getSecurity().getLoginResetTimeMinutes());
+ addIfNotEmpty(
+ properties,
+ "security_loginMethod",
+ applicationProperties.getSecurity().getLoginMethod());
+
+ // Capture OAuth2 properties (excluding sensitive information)
+ addIfNotEmpty(
+ properties,
+ "security_oauth2_enabled",
+ applicationProperties.getSecurity().getOauth2().getEnabled());
+ if (applicationProperties.getSecurity().getOauth2().getEnabled()) {
+ addIfNotEmpty(
+ properties,
+ "security_oauth2_autoCreateUser",
+ applicationProperties.getSecurity().getOauth2().getAutoCreateUser());
+ addIfNotEmpty(
+ properties,
+ "security_oauth2_blockRegistration",
+ applicationProperties.getSecurity().getOauth2().getBlockRegistration());
+ addIfNotEmpty(
+ properties,
+ "security_oauth2_useAsUsername",
+ applicationProperties.getSecurity().getOauth2().getUseAsUsername());
+ addIfNotEmpty(
+ properties,
+ "security_oauth2_provider",
+ applicationProperties.getSecurity().getOauth2().getProvider());
+ }
+ // Capture System properties
+ addIfNotEmpty(
+ properties,
+ "system_defaultLocale",
+ applicationProperties.getSystem().getDefaultLocale());
+ addIfNotEmpty(
+ properties,
+ "system_googlevisibility",
+ applicationProperties.getSystem().getGooglevisibility());
+ addIfNotEmpty(
+ properties, "system_showUpdate", applicationProperties.getSystem().isShowUpdate());
+ addIfNotEmpty(
+ properties,
+ "system_showUpdateOnlyAdmin",
+ applicationProperties.getSystem().getShowUpdateOnlyAdmin());
+ addIfNotEmpty(
+ properties,
+ "system_customHTMLFiles",
+ applicationProperties.getSystem().isCustomHTMLFiles());
+ addIfNotEmpty(
+ properties,
+ "system_tessdataDir",
+ applicationProperties.getSystem().getTessdataDir());
+ addIfNotEmpty(
+ properties,
+ "system_enableAlphaFunctionality",
+ applicationProperties.getSystem().getEnableAlphaFunctionality());
+ addIfNotEmpty(
+ properties,
+ "system_enableAnalytics",
+ applicationProperties.getSystem().getEnableAnalytics());
+
+ // Capture UI properties
+ addIfNotEmpty(properties, "ui_appName", applicationProperties.getUi().getAppName());
+ addIfNotEmpty(
+ properties,
+ "ui_homeDescription",
+ applicationProperties.getUi().getHomeDescription());
+ addIfNotEmpty(
+ properties, "ui_appNameNavbar", applicationProperties.getUi().getAppNameNavbar());
+
+ // Capture Metrics properties
+ addIfNotEmpty(
+ properties, "metrics_enabled", applicationProperties.getMetrics().getEnabled());
+
+ // Capture EnterpriseEdition properties
+ addIfNotEmpty(
+ properties,
+ "enterpriseEdition_enabled",
+ applicationProperties.getEnterpriseEdition().isEnabled());
+ if (applicationProperties.getEnterpriseEdition().isEnabled()) {
+ addIfNotEmpty(
+ properties,
+ "enterpriseEdition_customMetadata_autoUpdateMetadata",
+ applicationProperties
+ .getEnterpriseEdition()
+ .getCustomMetadata()
+ .isAutoUpdateMetadata());
+ addIfNotEmpty(
+ properties,
+ "enterpriseEdition_customMetadata_author",
+ applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor());
+ addIfNotEmpty(
+ properties,
+ "enterpriseEdition_customMetadata_creator",
+ applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator());
+ addIfNotEmpty(
+ properties,
+ "enterpriseEdition_customMetadata_producer",
+ applicationProperties.getEnterpriseEdition().getCustomMetadata().getProducer());
+ }
+ // Capture AutoPipeline properties
+ addIfNotEmpty(
+ properties,
+ "autoPipeline_outputFolder",
+ applicationProperties.getAutoPipeline().getOutputFolder());
+
+ return properties;
+ }
+
+ private String getMacAddress() {
+ try {
+ Enumeration networkInterfaces =
+ NetworkInterface.getNetworkInterfaces();
+ while (networkInterfaces.hasMoreElements()) {
+ NetworkInterface ni = networkInterfaces.nextElement();
+ byte[] hardwareAddress = ni.getHardwareAddress();
+ if (hardwareAddress != null) {
+ String[] hexadecimal = new String[hardwareAddress.length];
+ for (int i = 0; i < hardwareAddress.length; i++) {
+ hexadecimal[i] = String.format("%02X", hardwareAddress[i]);
+ }
+ return String.join("-", hexadecimal);
+ }
+ }
+ } catch (Exception e) {
+ // Handle exception
+ }
+ return "Unknown";
+ }
+
+ private Map getNetworkInterfacesInfo() {
+ Map interfacesInfo = new HashMap<>();
+ try {
+ Enumeration nets = NetworkInterface.getNetworkInterfaces();
+ while (nets.hasMoreElements()) {
+ NetworkInterface netint = nets.nextElement();
+ interfacesInfo.put(netint.getName(), netint.getDisplayName());
+ }
+ } catch (Exception e) {
+ interfacesInfo.put("error", e.getMessage());
+ }
+ return interfacesInfo;
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/utils/CustomHtmlSanitizer.java b/src/main/java/stirling/software/SPDF/utils/CustomHtmlSanitizer.java
new file mode 100644
index 00000000000..587aae6fa6b
--- /dev/null
+++ b/src/main/java/stirling/software/SPDF/utils/CustomHtmlSanitizer.java
@@ -0,0 +1,21 @@
+package stirling.software.SPDF.utils;
+
+import org.owasp.html.HtmlPolicyBuilder;
+import org.owasp.html.PolicyFactory;
+import org.owasp.html.Sanitizers;
+
+public class CustomHtmlSanitizer {
+ private static final PolicyFactory POLICY =
+ Sanitizers.FORMATTING
+ .and(Sanitizers.BLOCKS)
+ .and(Sanitizers.STYLES)
+ .and(Sanitizers.LINKS)
+ .and(Sanitizers.TABLES)
+ .and(Sanitizers.IMAGES)
+ .and(new HtmlPolicyBuilder().disallowElements("noscript").toFactory());
+
+ public static String sanitize(String html) {
+ String htmlAfter = POLICY.sanitize(html);
+ return htmlAfter;
+ }
+}
diff --git a/src/main/java/stirling/software/SPDF/utils/FileToPdf.java b/src/main/java/stirling/software/SPDF/utils/FileToPdf.java
index c712342445f..d18277df29a 100644
--- a/src/main/java/stirling/software/SPDF/utils/FileToPdf.java
+++ b/src/main/java/stirling/software/SPDF/utils/FileToPdf.java
@@ -2,16 +2,23 @@
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
import io.github.pixee.security.ZipSecurity;
@@ -33,19 +40,25 @@ public static byte[] convertHtmlToPdf(
try {
if (fileName.endsWith(".html")) {
tempInputFile = Files.createTempFile("input_", ".html");
- Files.write(tempInputFile, fileBytes);
- } else {
+ String sanitizedHtml =
+ sanitizeHtmlContent(new String(fileBytes, StandardCharsets.UTF_8));
+ Files.write(tempInputFile, sanitizedHtml.getBytes(StandardCharsets.UTF_8));
+ } else if (fileName.endsWith(".zip")) {
tempInputFile = Files.createTempFile("input_", ".zip");
Files.write(tempInputFile, fileBytes);
+ sanitizeHtmlFilesInZip(tempInputFile);
+ } else {
+ throw new IllegalArgumentException("Unsupported file format: " + fileName);
}
List command = new ArrayList<>();
if (!htmlFormatsInstalled) {
command.add("weasyprint");
- command.add("-e utf-8");
+ command.add("-e");
+ command.add("utf-8");
+ command.add("-v");
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());
-
} else {
command.add("ebook-convert");
command.add(tempInputFile.toString());
@@ -54,10 +67,8 @@ public static byte[] convertHtmlToPdf(
command.add("a4");
if (request != null && request.getZoom() != 1.0) {
- // Create a temporary CSS file
File tempCssFile = Files.createTempFile("customStyle", ".css").toFile();
try (FileWriter writer = new FileWriter(tempCssFile)) {
- // Write the CSS rule to the file
writer.write("body { zoom: " + request.getZoom() + "; }");
}
command.add("--extra-css");
@@ -65,9 +76,7 @@ public static byte[] convertHtmlToPdf(
}
}
- ProcessExecutorResult returnCode;
-
- returnCode =
+ ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
.runCommandWithOutputHandling(command);
@@ -78,8 +87,6 @@ public static byte[] convertHtmlToPdf(
throw e;
}
} finally {
-
- // Clean up temporary files
Files.deleteIfExists(tempOutputFile);
Files.deleteIfExists(tempInputFile);
}
@@ -87,6 +94,81 @@ public static byte[] convertHtmlToPdf(
return pdfBytes;
}
+ private static String sanitizeHtmlContent(String htmlContent) {
+ return CustomHtmlSanitizer.sanitize(htmlContent);
+ }
+
+ private static void sanitizeHtmlFilesInZip(Path zipFilePath) throws IOException {
+ Path tempUnzippedDir = Files.createTempDirectory("unzipped_");
+ try (ZipInputStream zipIn =
+ ZipSecurity.createHardenedInputStream(
+ new ByteArrayInputStream(Files.readAllBytes(zipFilePath)))) {
+ ZipEntry entry = zipIn.getNextEntry();
+ while (entry != null) {
+ Path filePath = tempUnzippedDir.resolve(entry.getName());
+ if (!entry.isDirectory()) {
+ Files.createDirectories(filePath.getParent());
+ if (entry.getName().toLowerCase().endsWith(".html")
+ || entry.getName().toLowerCase().endsWith(".htm")) {
+ String content = new String(zipIn.readAllBytes(), StandardCharsets.UTF_8);
+ String sanitizedContent = sanitizeHtmlContent(content);
+ Files.write(filePath, sanitizedContent.getBytes(StandardCharsets.UTF_8));
+ } else {
+ Files.copy(zipIn, filePath);
+ }
+ }
+ zipIn.closeEntry();
+ entry = zipIn.getNextEntry();
+ }
+ }
+
+ // Repack the sanitized files
+ zipDirectory(tempUnzippedDir, zipFilePath);
+
+ // Clean up
+ deleteDirectory(tempUnzippedDir);
+ }
+
+ private static void zipDirectory(Path sourceDir, Path zipFilePath) throws IOException {
+ try (ZipOutputStream zos =
+ new ZipOutputStream(new FileOutputStream(zipFilePath.toFile()))) {
+ Files.walk(sourceDir)
+ .filter(path -> !Files.isDirectory(path))
+ .forEach(
+ path -> {
+ ZipEntry zipEntry =
+ new ZipEntry(sourceDir.relativize(path).toString());
+ try {
+ zos.putNextEntry(zipEntry);
+ Files.copy(path, zos);
+ zos.closeEntry();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ }
+ }
+
+ private static void deleteDirectory(Path dir) throws IOException {
+ Files.walkFileTree(
+ dir,
+ new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+ throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+ throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
private static Path unzipAndGetMainHtml(byte[] fileBytes) throws IOException {
Path tempDirectory = Files.createTempDirectory("unzipped_");
try (ZipInputStream zipIn =
diff --git a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
index 21d921c833e..8e56c8df6e8 100644
--- a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
+++ b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
@@ -5,18 +5,28 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
+import java.net.InetAddress;
import java.net.MalformedURLException;
+import java.net.NetworkInterface;
import java.net.URI;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
+import java.security.MessageDigest;
import java.util.ArrayList;
+import java.util.Enumeration;
import java.util.List;
+import java.util.UUID;
+import org.simpleyaml.configuration.file.YamlFile;
+import org.simpleyaml.configuration.file.YamlFileWrapper;
+import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
+import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
@@ -262,4 +272,81 @@ public static boolean createDir(String path) {
}
return true;
}
+
+ public static boolean isValidUUID(String uuid) {
+ if (uuid == null) {
+ return false;
+ }
+ try {
+ UUID.fromString(uuid);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ public static void saveKeyToConfig(String id, String key) throws IOException {
+ saveKeyToConfig(id, key, true);
+ }
+
+ public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
+ throws IOException {
+ Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
+
+ final YamlFile settingsYml = new YamlFile(path.toFile());
+ DumperOptions yamlOptionssettingsYml =
+ ((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
+ yamlOptionssettingsYml.setSplitLines(false);
+
+ settingsYml.loadWithComments();
+
+ YamlFileWrapper writer = settingsYml.path(id).set(key);
+ if (autoGenerated) {
+ writer.comment("# Automatically Generated Settings (Do Not Edit Directly)");
+ }
+ settingsYml.save();
+ }
+
+ public static String generateMachineFingerprint() {
+ try {
+ // Get the MAC address
+ StringBuilder sb = new StringBuilder();
+ InetAddress ip = InetAddress.getLocalHost();
+ NetworkInterface network = NetworkInterface.getByInetAddress(ip);
+
+ if (network == null) {
+ Enumeration networks = NetworkInterface.getNetworkInterfaces();
+ while (networks.hasMoreElements()) {
+ NetworkInterface net = networks.nextElement();
+ byte[] mac = net.getHardwareAddress();
+ if (mac != null) {
+ for (int i = 0; i < mac.length; i++) {
+ sb.append(String.format("%02X", mac[i]));
+ }
+ break; // Use the first network interface with a MAC address
+ }
+ }
+ } else {
+ byte[] mac = network.getHardwareAddress();
+ if (mac != null) {
+ for (int i = 0; i < mac.length; i++) {
+ sb.append(String.format("%02X", mac[i]));
+ }
+ }
+ }
+
+ // Hash the MAC address for privacy and consistency
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ byte[] hash = md.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
+ StringBuilder fingerprint = new StringBuilder();
+ for (byte b : hash) {
+ fingerprint.append(String.format("%02x", b));
+ }
+
+ return fingerprint.toString();
+
+ } catch (Exception e) {
+ return "GenericID";
+ }
+ }
}
diff --git a/src/main/java/stirling/software/SPDF/utils/PDFToFile.java b/src/main/java/stirling/software/SPDF/utils/PDFToFile.java
index 920e987b8d1..1a1957cf715 100644
--- a/src/main/java/stirling/software/SPDF/utils/PDFToFile.java
+++ b/src/main/java/stirling/software/SPDF/utils/PDFToFile.java
@@ -191,7 +191,6 @@ public ResponseEntity processPdfToOfficeFormat(
Files.deleteIfExists(tempInputFile);
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
}
- System.out.println("fileBytes=" + fileBytes.length);
return WebResponseUtils.bytesToWebResponse(
fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
}
diff --git a/src/main/java/stirling/software/SPDF/utils/RequestUriUtils.java b/src/main/java/stirling/software/SPDF/utils/RequestUriUtils.java
index 7aeab8e177f..7033fa7a706 100644
--- a/src/main/java/stirling/software/SPDF/utils/RequestUriUtils.java
+++ b/src/main/java/stirling/software/SPDF/utils/RequestUriUtils.java
@@ -17,6 +17,7 @@ public static boolean isStaticResource(String contextPath, String requestURI) {
|| requestURI.startsWith(contextPath + "/public/")
|| requestURI.startsWith(contextPath + "/pdfjs/")
|| requestURI.startsWith(contextPath + "/login")
+ || requestURI.startsWith(contextPath + "/error")
|| requestURI.endsWith(".svg")
|| requestURI.endsWith(".png")
|| requestURI.endsWith(".ico")
diff --git a/src/main/java/stirling/software/SPDF/utils/misc/ReplaceAndInvertColorStrategy.java b/src/main/java/stirling/software/SPDF/utils/misc/ReplaceAndInvertColorStrategy.java
index 87590731465..024e0e706f8 100644
--- a/src/main/java/stirling/software/SPDF/utils/misc/ReplaceAndInvertColorStrategy.java
+++ b/src/main/java/stirling/software/SPDF/utils/misc/ReplaceAndInvertColorStrategy.java
@@ -6,11 +6,12 @@
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
+import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
@Data
-// @EqualsAndHashCode(callSuper = true)
+@EqualsAndHashCode(callSuper = true)
public abstract class ReplaceAndInvertColorStrategy extends PDFFile {
protected ReplaceAndInvert replaceAndInvert;
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index cae1dce3d4a..0f957c0a997 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -27,9 +27,9 @@ server.servlet.context-path=${SYSTEM_ROOTURIPATH:/}
spring.devtools.restart.enabled=true
spring.devtools.livereload.enabled=true
-
spring.thymeleaf.encoding=UTF-8
+spring.web.resources.mime-mappings.webmanifest=application/manifest+json
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
@@ -41,7 +41,7 @@ spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=false
spring.jpa.hibernate.ddl-auto=update
-
+server.servlet.session.timeout: 30m
# Change the default URL path for OpenAPI JSON
springdoc.api-docs.path=/v1/api-docs
@@ -49,3 +49,5 @@ springdoc.api-docs.path=/v1/api-docs
springdoc.swagger-ui.url=/v1/api-docs
+posthog.api.key=phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq
+posthog.host=https://eu.i.posthog.com
\ No newline at end of file
diff --git a/src/main/resources/messages_ar_AR.properties b/src/main/resources/messages_ar_AR.properties
index 19fe9944553..275557428d3 100644
--- a/src/main/resources/messages_ar_AR.properties
+++ b/src/main/resources/messages_ar_AR.properties
@@ -76,6 +76,7 @@ donate=تبرع
color=لون
sponsor=راعٍ
info=معلومات
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=خط الأنابيب:
pipelineOptions.saveButton=تنزيل
pipelineOptions.validateButton=تحقق
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=تحويل من PDF
navbar.sections.security=التوقيع والأمان
navbar.sections.advance=متقدم
navbar.sections.edit=عرض وتعديل
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=لم يتم العثور على الملف
database.fileNullOrEmpty=يجب ألا يكون الملف فارغًا أو خاليًا
database.failedImportFile=فشل استيراد الملف
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=ضبط حجم/مقياس الصفحة
home.scalePages.desc=تغيير حجم/مقياس الصفحة و/أو محتواها.
scalePages.tags=تغيير الحجم,تعديل,الأبعاد,تكييف
-home.pipeline.title=خط الأنابيب (متقدم)
+home.pipeline.title=خط الأنابيب
home.pipeline.desc=تشغيل إجراءات متعددة على ملفات PDF عن طريق تحديد نصوص خط الأنابيب
pipeline.tags=أتمتة,تسلسل,مبرمج,معالجة دفعات
@@ -482,6 +502,11 @@ home.removeImagePdf.title=إزالة الصورة
home.removeImagePdf.desc=إزالة الصورة من PDF لتقليل حجم الملف
removeImagePdf.tags=إزالة الصورة,عمليات الصفحة,الخلفية,جانب الخادم
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=تم رفض الوصول
login.oauth2InvalidTokenResponse=استجابة الرمز المميز غير صالحة
login.oauth2InvalidIdToken=رمز الهوية غير صالح
login.userIsDisabled=تم تعطيل المستخدم، تم حظر تسجيل الدخول حاليًا باستخدام اسم المستخدم هذا. يرجى الاتصال بالمسؤول.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=حجب تلقائي
@@ -1154,6 +1181,8 @@ licenses.license=الترخيص
survey.nav=استطلاع
survey.title=استطلاع Stirling-PDF
survey.description=Stirling-PDF لا يحتوي على تتبع لذا نريد أن نسمع من مستخدمينا لتحسين Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=يرجى النظر في المشاركة في استطلاعنا!
survey.disabled=(سيتم تعطيل النافذة المنبثقة للاستطلاع في التحديثات التالية ولكنها ستكون متاحة في أسفل الصفحة)
survey.button=المشاركة في الاستطلاع
@@ -1179,3 +1208,17 @@ removeImage.title=إزالة الصورة
removeImage.header=إزالة الصورة
removeImage.removeImage=إزالة الصورة
removeImage.submit=إزالة الصورة
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_bg_BG.properties b/src/main/resources/messages_bg_BG.properties
index 2e224ba7494..c1f7a10d329 100644
--- a/src/main/resources/messages_bg_BG.properties
+++ b/src/main/resources/messages_bg_BG.properties
@@ -3,8 +3,8 @@
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
-addPageNumbers.fontSize=Font Size
-addPageNumbers.fontName=Font Name
+addPageNumbers.fontSize=Размер на шрифт
+addPageNumbers.fontName=Име на шрифт
pdfPrompt=Изберете PDF(и)
multiPdfPrompt=Изберете PDF (2+)
multiPdfDropPrompt=Изберете (или плъзнете и пуснете) всички PDF файлове, от които се нуждаете
@@ -56,12 +56,12 @@ userNotFoundMessage=Потребителят не е намерен
incorrectPasswordMessage=Текущата парола е неправилна.
usernameExistsMessage=Новият потребител вече съществува.
invalidUsernameMessage=Невалидно потребителско име, потребителското име може да съдържа само букви, цифри и следните специални знаци @._+- или трябва да е валиден имейл адрес.
-invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
-confirmPasswordErrorMessage=New Password and Confirm New Password must match.
+invalidPasswordMessage=Паролата не трябва да е празна и не трябва да има интервали в началото или в края.
+confirmPasswordErrorMessage=Нова парола и Потвърждаване на новата парола трябва да съвпадат.
deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител.
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
-disabledCurrentUserMessage=The current user cannot be disabled
+disabledCurrentUserMessage=Текущият потребител не може да бъде деактивиран
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
userAlreadyExistsOAuthMessage=Потребителят вече съществува като OAuth2 потребител.
userAlreadyExistsWebMessage=Потребителят вече съществува като уеб-потребител.
@@ -75,15 +75,16 @@ visitGithub=Посетете Github Repository
donate=Направете дарение
color=Цвят
sponsor=Спонсор
-info=Info
-page=Page
-pages=Pages
+info=Информация
+pro=Pro
+page=Страница
+pages=Страници
-legal.privacy=Privacy Policy
-legal.terms=Terms and Conditions
-legal.accessibility=Accessibility
-legal.cookie=Cookie Policy
-legal.impressum=Impressum
+legal.privacy=Политика за поверителност
+legal.terms=Правила и условия
+legal.accessibility=Достъпност
+legal.cookie=Политика за бисквитки
+legal.impressum=Отпечатък
###############
# Pipeline #
@@ -95,7 +96,7 @@ pipeline.defaultOption=Персонализиран
pipeline.submitButton=Подайте
pipeline.help=Pipeline Помощ
pipeline.scanHelp=Помощ за сканиране на папки
-pipeline.deletePrompt=Are you sure you want to delete pipeline
+pipeline.deletePrompt=Сигурни ли сте, че искате да изтриете pipeline
######################
# Pipeline Options #
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Изтегли
pipelineOptions.validateButton=Валидирай
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Направете надстройка до Pro версията
+enterpriseEdition.warning=Тази функция е достъпна само за потребители на Pro версията.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro поддържа YAML конфигурационни файлове и други SSO функции.
+enterpriseEdition.ssoAdvert=Търсите повече функции за управление на потребителите? Погледнете за Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Искате ли да подобрите Stirling PDF?
+analytics.paragraph1=Stirling PDF включва анализи, за да ни помогне да подобрим продукта. Ние не проследяваме лична информация или съдържание на файлове.
+analytics.paragraph2=Моля, обмислете възможността за анализ, за да помогнете на Stirling-PDF да расте и да ни позволи да разберем по-добре нашите потребители.
+analytics.enable=Активиране на анализа
+analytics.disable=Деактивиране на анализа
+analytics.settings=Можете да промените настройките за анализ във config/settings.yml файла
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Преобразуване от PDF
navbar.sections.security=Подписване и сигурност
navbar.sections.advance=Разширено
navbar.sections.edit=Преглед и редактиране
+navbar.sections.popular=Популярни
#############
# SETTINGS #
@@ -184,9 +202,9 @@ adminUserSettings.header=Настройки за администраторск
adminUserSettings.admin=Администратор
adminUserSettings.user=Потребител
adminUserSettings.addUser=Добавяне на нов потребител
-adminUserSettings.deleteUser=Delete User
-adminUserSettings.confirmDeleteUser=Should the user be deleted?
-adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
+adminUserSettings.deleteUser=Изтриване на потребител
+adminUserSettings.confirmDeleteUser=Трябва ли потребителят да бъде изтрит?
+adminUserSettings.confirmChangeUserStatus=Трябва ли потребителят да бъде деактивиран/активиран?
adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес.
adminUserSettings.roles=Роли
adminUserSettings.role=Роля
@@ -200,30 +218,32 @@ adminUserSettings.forceChange=Принудете потребителя да п
adminUserSettings.submit=Съхранете потребителя
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
adminUserSettings.authenticated=Удостоверен
-adminUserSettings.editOwnProfil=Edit own profile
-adminUserSettings.enabledUser=enabled user
-adminUserSettings.disabledUser=disabled user
-adminUserSettings.activeUsers=Active Users:
-adminUserSettings.disabledUsers=Disabled Users:
-adminUserSettings.totalUsers=Total Users:
-adminUserSettings.lastRequest=Last Request
-
-
-database.title=Database Import/Export
-database.header=Database Import/Export
-database.fileName=File Name
-database.creationDate=Creation Date
-database.fileSize=File Size
-database.deleteBackupFile=Delete Backup File
-database.importBackupFile=Import Backup File
-database.downloadBackupFile=Download Backup File
-database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
-database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
-database.submit=Import Backup
-database.importIntoDatabaseSuccessed=Import into database successed
-database.fileNotFound=File not Found
-database.fileNullOrEmpty=File must not be null or empty
-database.failedImportFile=Failed Import File
+adminUserSettings.editOwnProfil=Редактиране на собствен профил
+adminUserSettings.enabledUser=активиран потребител
+adminUserSettings.disabledUser=деактивиран потребител
+adminUserSettings.activeUsers=Активни потребители:
+adminUserSettings.disabledUsers=Деактивирани потребители:
+adminUserSettings.totalUsers=Общо потребители:
+adminUserSettings.lastRequest=Последна заявка
+
+
+database.title=Импорт/Експорт на база данни
+database.header=Импорт/Експорт на база данни
+database.fileName=Име на файл
+database.creationDate=Дата на създаване
+database.fileSize=Размер на файла
+database.deleteBackupFile=Изтриване на архивен файл
+database.importBackupFile=Импортиране на архивен файл
+database.downloadBackupFile=Изтеглете архивен файл
+database.info_1=Когато импортирате данни, е от решаващо значение да осигурите правилната структура. Ако не сте сигурни в това, което правите, потърсете съвет и подкрепа от професионалист. Грешка в структурата може да причини неизправност на приложението, включително пълна невъзможност за стартиране на приложението.
+database.info_2=Името на файла няма значение при качване. След това ще бъде преименуван, за да следва формата backup_user_yyyyMMddHHmm.sql, осигурявайки последователна конвенция за именуване.
+database.submit=Импортиране на резервно копие
+database.importIntoDatabaseSuccessed=Импортирането в базата данни бе успешно
+database.fileNotFound=Файлът не е намерен
+database.fileNullOrEmpty=Файлът не трябва да е нулев или празен
+database.failedImportFile=Неуспешно импортиране на файл
+
+session.expired=Вашата сесия е изтекла. Моля, опреснете страницата и опитайте отново.
#############
# HOME-PAGE #
@@ -370,9 +390,9 @@ home.certSign.title=Подпишете със сертификат
home.certSign.desc=Подписва PDF със сертификат/ключ (PEM/P12)
certSign.tags=удостоверяване,PEM,P12,официален,шифроване
-home.removeCertSign.title=Remove Certificate Sign
-home.removeCertSign.desc=Remove certificate signature from PDF
-removeCertSign.tags=authenticate,PEM,P12,official,decrypt
+home.removeCertSign.title=Премахване на знака за сертификат
+home.removeCertSign.desc=Премахване на подпис на сертификат от PDF
+removeCertSign.tags=удостоверяване,PEM,P12,официален,декриптиране
home.pageLayout.title=Оформление с няколко страници
home.pageLayout.desc=Слейте няколко страници от PDF документ в една страница
@@ -478,28 +498,33 @@ home.BookToPDF.title=Книга към PDF
home.BookToPDF.desc=Преобразува формати на книги/комикси в PDF с помощта на calibre
BookToPDF.tags=Книга,комикс,calibre,конвертиране,манга,Amazon,Kindle
-home.removeImagePdf.title=Remove image
-home.removeImagePdf.desc=Remove image from PDF to reduce file size
-removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+home.removeImagePdf.title=Премахване на изображение
+home.removeImagePdf.desc=Премахнете изображението от PDF, за да намалите размера на файла
+removeImagePdf.tags=Премахване на изображение, операции на страници, админ страна, страна на сървъра
+
+
+home.splitPdfByChapters.title=Разделете PDF по глави
+home.splitPdfByChapters.desc=Разделете PDF на множество файлове въз основа на неговата структура на глави.
+splitPdfByChapters.tags=разделяне, глави, отметки, организиране
#replace-invert-color
-replace-color.title=Replace-Invert-Color
-replace-color.header=Replace-Invert Color PDF
-home.replaceColorPdf.title=Replace and Invert Color
-home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
-replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
-replace-color.selectText.1=Replace or Invert color Options
-replace-color.selectText.2=Default(Default high contrast colors)
-replace-color.selectText.3=Custom(Customized colors)
-replace-color.selectText.4=Full-Invert(Invert all colors)
-replace-color.selectText.5=High contrast color options
-replace-color.selectText.6=white text on black background
-replace-color.selectText.7=Black text on white background
-replace-color.selectText.8=Yellow text on black background
-replace-color.selectText.9=Green text on black background
-replace-color.selectText.10=Choose text Color
-replace-color.selectText.11=Choose background Color
-replace-color.submit=Replace
+replace-color.title=Замени-инвертиране-на-цвят
+replace-color.header=Замяна-инвертиране на цвят PDF
+home.replaceColorPdf.title=Замяна и обръщане на цвят
+home.replaceColorPdf.desc=Заменете цвета на текста и фона в PDF и обърнете пълния цвят на PDF, за да намалите размера на файла
+replaceColorPdf.tags=Замяна на цвят, операции на страници, заден край, страна на сървъра
+replace-color.selectText.1=Опции за замяна или инвертиране на цвят
+replace-color.selectText.2=По подразбиране (цветове с висок контраст по подразбиране)
+replace-color.selectText.3=По избор (персонализирани цветове)
+replace-color.selectText.4=Пълно инвертиране (Инвертиране на всички цветове)
+replace-color.selectText.5=Цветови опции с висок контраст
+replace-color.selectText.6=Бял текст на черен фон
+replace-color.selectText.7=Черен текст на бял фон
+replace-color.selectText.8=Жълт текст на черен фон
+replace-color.selectText.9=Зелен текст на черен фон
+replace-color.selectText.10=Изберете цвят на текста
+replace-color.selectText.11=Изберете цвят на фона
+replace-color.submit=Замени
@@ -518,15 +543,17 @@ login.locked=Вашият акаунт е заключен.
login.signinTitle=Моля впишете се
login.ssoSignIn=Влизане чрез еднократно влизане
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
-login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
+login.oauth2AdminBlockedUser=Регистрацията или влизането на нерегистрирани потребители в момента е блокирано. Моля, свържете се с администратора.
login.oauth2RequestNotFound=Заявката за оторизация не е намерена
login.oauth2InvalidUserInfoResponse=Невалидна информация за потребителя
login.oauth2invalidRequest=Невалидна заявка
login.oauth2AccessDenied=Отказан достъп
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
-login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.userIsDisabled=Потребителят е деактивиран, влизането в момента е блокирано с това потребителско име. Моля, свържете се с администратора.
+login.alreadyLoggedIn=Вече сте влезли в
+login.alreadyLoggedIn2=устройства. Моля, излезте от устройствата и опитайте отново.
+login.toManySessions=Имате твърде много активни сесии
#auto-redact
autoRedact.title=Автоматично редактиране
@@ -701,7 +728,7 @@ pageLayout.submit=Подайте
scalePages.title=Коригиране на мащаба на страницата
scalePages.header=Коригиране на мащаба на страницата
scalePages.pageSize=Размер на страница от документа.
-scalePages.keepPageSize=Original Size
+scalePages.keepPageSize=Оригинален размер
scalePages.scaleFactor=Ниво на мащабиране (изрязване) на страница.
scalePages.submit=Подайте
@@ -725,10 +752,10 @@ certSign.submit=Подпишете PDF
#removeCertSign
-removeCertSign.title=Remove Certificate Signature
-removeCertSign.header=Remove the digital certificate from the PDF
-removeCertSign.selectPDF=Select a PDF file:
-removeCertSign.submit=Remove Signature
+removeCertSign.title=Премахване на подписа на сертификата
+removeCertSign.header=Премахнете цифровия сертификат от PDF
+removeCertSign.selectPDF=Изберете PDF файл:
+removeCertSign.submit=Премахване на подпис
#removeBlanks
@@ -750,8 +777,8 @@ removeAnnotations.submit=Премахване
#compare
compare.title=Сравнявай
compare.header=Сравнявай PDF-и
-compare.highlightColor.1=Highlight Color 1:
-compare.highlightColor.2=Highlight Color 2:
+compare.highlightColor.1=Цвят на маркирането 1:
+compare.highlightColor.2=Цвят на маркирането 2:
compare.document.1=Документ 1
compare.document.2=Документ 2
compare.submit=Сравнявай
@@ -803,7 +830,7 @@ ScannerImageSplit.selectText.7=Минимална контурна площ:
ScannerImageSplit.selectText.8=Задава минималния праг на контурната площ за изображение
ScannerImageSplit.selectText.9=Размер на рамката:
ScannerImageSplit.selectText.10=Задава размера на добавената и премахната граница, за да предотврати бели граници към изхода (по подразбиране: 1).
-ScannerImageSplit.info=Python is not installed. It is required to run.
+ScannerImageSplit.info=Python не е инсталиран. Изисква се да се изпълнява.
#OCR
@@ -830,7 +857,7 @@ ocr.submit=Обработка на PDF чрез OCR
extractImages.title=Извличане на изображения
extractImages.header=Извличане на изображения
extractImages.selectText=Изберете формат на изображението, в който да преобразувате извлечените изображения
-extractImages.allowDuplicates=Save duplicate images
+extractImages.allowDuplicates=Запазване на дублирани изображения
extractImages.submit=Извличане
@@ -868,7 +895,7 @@ merge.title=Обединяване
merge.header=Обединяване на множество PDF файлове (2+)
merge.sortByName=Сортиране по име
merge.sortByDate=Сортиране по дата
-merge.removeCertSign=Remove digital signature in the merged file?
+merge.removeCertSign=Премахване на цифровия подпис в обединения файл?
merge.submit=Обединяване
@@ -886,7 +913,7 @@ pdfOrganiser.mode.6=Четно-нечетно разделяне
pdfOrganiser.mode.7=Премахни първо
pdfOrganiser.mode.8=Премахване на последния
pdfOrganiser.mode.9=Премахване на първия и последния
-pdfOrganiser.mode.10=Odd-Even Merge
+pdfOrganiser.mode.10=Обединяване на четно и нечетно
pdfOrganiser.placeholder=(напр. 1,3,2 или 4-8,2,10-12 или 2n-1)
@@ -955,7 +982,7 @@ pdfToImage.color=Цвят
pdfToImage.grey=Скала на сивото
pdfToImage.blackwhite=Черно и бяло (може да загубите данни!)
pdfToImage.submit=Преобразуване
-pdfToImage.info=Python is not installed. Required for WebP conversion.
+pdfToImage.info=Python не е инсталиран. Изисква се за конвертиране на WebP.
#addPassword
@@ -992,7 +1019,7 @@ watermark.selectText.6=дължинаSpacer (Разстояние между в
watermark.selectText.7=Непрозрачност (0% - 100%):
watermark.selectText.8=Тип воден знак:
watermark.selectText.9=Изображение за воден знак:
-watermark.selectText.10=Convert PDF to PDF-Image
+watermark.selectText.10=Конвертирайте PDF в PDF-изображение
watermark.submit=Добавяне на воден знак
watermark.type.1=Текст
watermark.type.2=Изображение
@@ -1049,7 +1076,7 @@ pdfToPDFA.credit=Тази услуга използва ghostscript за PDF/A
pdfToPDFA.submit=Преобразуване
pdfToPDFA.tip=В момента не работи за няколко входа наведнъж
pdfToPDFA.outputFormat=Изходен формат
-pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
+pdfToPDFA.pdfWithDigitalSignature=PDF файлът съдържа цифров подпис. Това ще бъде премахнато в следващата стъпка.
#PDFToWord
@@ -1090,10 +1117,10 @@ PDFToXML.credit=Тази услуга използва LibreOffice за прео
PDFToXML.submit=Преобразуване
#PDFToCSV
-PDFToCSV.title=PDF ??? CSV
-PDFToCSV.header=PDF ??? CSV
+PDFToCSV.title=PDF към CSV
+PDFToCSV.header=PDF към CSV
PDFToCSV.prompt=Изберете страница за извличане на таблица
-PDFToCSV.submit=????
+PDFToCSV.submit=Преобразуване
#split-by-size-or-count
split-by-size-or-count.title=Разделяне на PDF по размер или брой
@@ -1151,13 +1178,15 @@ licenses.version=Версия
licenses.license=Лиценз
#survey
-survey.nav=Survey
-survey.title=Stirling-PDF Survey
-survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
-survey.please=Please consider taking our survey!
-survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
-survey.button=Take Survey
-survey.dontShowAgain=Don't show again
+survey.nav=Анкета
+survey.title=Stirling-PDF Анкета
+survey.description=Stirling-PDF няма проследяване, така че искаме да чуем мнението на нашите потребители за подобряване на Stirling-PDF!
+survey.changes=Stirling-PDF се промени от последното проучване! За да научите повече, моля, проверете публикацията в нашия блог тук:
+survey.changes2=С тези промени получаваме платена бизнес подкрепа и финансиране
+survey.please=Моля, помислете дали да не участвате в нашата анкета!
+survey.disabled=(Изскачащият прозорец с анкетата ще бъде деактивиран при следващите актуализации, но ще бъде наличен в долната част на страницата)
+survey.button=Участвайте в анкетата
+survey.dontShowAgain=Не показвай повече
#error
@@ -1175,7 +1204,21 @@ error.discordSubmit=Discord - Изпратете запитване за под
#remove-image
-removeImage.title=Remove image
-removeImage.header=Remove image
-removeImage.removeImage=Remove image
-removeImage.submit=Remove image
+removeImage.title=Премахване на изображението
+removeImage.header=Премахване на изображението
+removeImage.removeImage=Премахване на изображението
+removeImage.submit=Премахване на изображението
+
+
+splitByChapters.title=Разделете PDF по глави
+splitByChapters.header=Разделете PDF по глави
+splitByChapters.bookmarkLevel=Ниво на отметка
+splitByChapters.includeMetadata=Включете метаданни
+splitByChapters.allowDuplicates=Разрешаване на дубликати
+splitByChapters.desc.1=Този инструмент разделя PDF файл на множество PDF файлове въз основа на неговата структура на глави.
+splitByChapters.desc.2=Ниво на отметка: Изберете нивото на отметките, които да използвате за разделяне (0 за най-високо ниво, 1 за второ ниво и т.н.).
+splitByChapters.desc.3=Включване на метаданни: Ако е отметнато, метаданните на оригиналния PDF ще бъдат включени във всеки разделен PDF.
+splitByChapters.desc.4=Разрешаване на дубликати: Ако е отметнато, позволява множество отметки на една и съща страница за създаване на отделни PDF файлове.
+splitByChapters.submit=Разделяне на PDF
+
+
diff --git a/src/main/resources/messages_ca_CA.properties b/src/main/resources/messages_ca_CA.properties
index 48ffcb693cb..71d30fa730c 100644
--- a/src/main/resources/messages_ca_CA.properties
+++ b/src/main/resources/messages_ca_CA.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
scalePages.tags=resize,modify,dimension,adapt
-home.pipeline.title=Pipeline (Advanced)
+home.pipeline.title=Pipeline
home.pipeline.desc=Run multiple actions on PDFs by defining pipeline scripts
pipeline.tags=automate,sequence,scripted,batch-process
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Redact
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_cs_CZ.properties b/src/main/resources/messages_cs_CZ.properties
index 4f4ec782132..b266097f803 100644
--- a/src/main/resources/messages_cs_CZ.properties
+++ b/src/main/resources/messages_cs_CZ.properties
@@ -76,6 +76,7 @@ donate=Přispějte
color=Barva
sponsor=Sponzor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Stáhnout
pipelineOptions.validateButton=Ověřit
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Převést z PDF
navbar.sections.security=Podpis a Bezpečnost
navbar.sections.advance=Pokročilé
navbar.sections.edit=Prohlédnout a Upravit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Upravit velikost/škálu stránky
home.scalePages.desc=Změnit velikost/škálu stránky a/nebo její obsah.
scalePages.tags=změnit velikost,upravit,rozměr,přizpůsobit
-home.pipeline.title=Potrubí (Pokročilé)
+home.pipeline.title=Potrubí
home.pipeline.desc=Spustit více akcí na PDF s definicí skriptů potrubí
pipeline.tags=automatizovat,sekvence,skriptované,dávkové zpracování
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Redact
@@ -1154,6 +1181,8 @@ licenses.license=Licence
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_da_DK.properties b/src/main/resources/messages_da_DK.properties
index 7e2c4538d9e..22348c345ef 100644
--- a/src/main/resources/messages_da_DK.properties
+++ b/src/main/resources/messages_da_DK.properties
@@ -76,6 +76,7 @@ donate=Donér
color=Farve
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validér
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Konvertér fra PDF
navbar.sections.security=Signér & Sikkerhed
navbar.sections.advance=Avanceret
navbar.sections.edit=Vis & Redigér
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Fil ikke fundet
database.fileNullOrEmpty=Fil må ikke være null eller tom
database.failedImportFile=Kunne ikke importere fil
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Fjern billede
home.removeImagePdf.desc=Fjern billede fra PDF for at reducere filstørrelse
removeImagePdf.tags=Fjern Billede,Sideoperationer,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Adgang Nægtet
login.oauth2InvalidTokenResponse=Ugyldigt Token Svar
login.oauth2InvalidIdToken=Ugyldigt Id Token
login.userIsDisabled=Bruger er deaktiveret, login er i øjeblikket blokeret med dette brugernavn. Kontakt venligst administratoren.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Rediger
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Undersøgelse
survey.title=Stirling-PDF Undersøgelse
survey.description=Stirling-PDF har ingen sporing, så vi vil gerne høre fra vores brugere for at forbedre Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Overvej venligst at deltage i vores undersøgelse!
survey.disabled=(Undersøgelsespop-up vil blive deaktiveret i følgende opdateringer, men vil være tilgængelig i bunden af siden)
survey.button=Tag Undersøgelsen
@@ -1179,3 +1208,17 @@ removeImage.title=Fjern billede
removeImage.header=Fjern billede
removeImage.removeImage=Fjern billede
removeImage.submit=Fjern
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties
index e156dbf2a0b..8467edd067a 100644
--- a/src/main/resources/messages_de_DE.properties
+++ b/src/main/resources/messages_de_DE.properties
@@ -76,6 +76,7 @@ donate=Spenden
color=Farbe
sponsor=Sponsor
info=Informationen
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Herunterladen
pipelineOptions.validateButton=Validieren
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Konvertieren von PDF
navbar.sections.security=Zeichen und Sicherheit
navbar.sections.advance=Fortschrittlich
navbar.sections.edit=Anzeigen und Bearbeiten
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Datei nicht gefunden
database.fileNullOrEmpty=Datei darf nicht null oder leer sein
database.failedImportFile=Dateiimport fehlgeschlagen
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Seitengröße/Skalierung anpassen
home.scalePages.desc=Größe/Skalierung der Seite und/oder des Inhalts ändern
scalePages.tags=größe ändern,ändern,dimensionieren,anpassen
-home.pipeline.title=Pipeline (Fortgeschritten)
+home.pipeline.title=Pipeline
home.pipeline.desc=Mehrere Aktionen auf ein PDF anwenden, definiert durch ein Pipeline Skript
pipeline.tags=automatisieren,sequenzieren,skriptgesteuert,batch prozess
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Bild entfernen
home.removeImagePdf.desc=Bild aus PDF entfernen, um die Dateigröße zu verringern
removeImagePdf.tags=bild entfernen,seitenoperationen,back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Zugriff abgelehnt
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
login.oauth2InvalidIdToken=Ungültiges ID-Token
login.userIsDisabled=Benutzer ist deaktiviert, die Anmeldung ist mit diesem Benutzernamen derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Automatisch zensieren/schwärzen
@@ -1154,6 +1181,8 @@ licenses.license=Lizenz
survey.nav=Umfrage
survey.title=Stirling-PDF-Umfrage
survey.description=Stirling-PDF hat kein Tracking, daher möchten wir von unseren Benutzern hören, wie wir Stirling-PDF verbessern können!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Bitte nehmen Sie an unserer Umfrage teil!
survey.disabled=(Das Umfrage-Popup wird in folgenden Updates deaktiviert, ist aber am Fuß der Seite verfügbar.)
survey.button=Umfrage durchführen
@@ -1179,3 +1208,17 @@ removeImage.title=Bild entfernen
removeImage.header=Bild entfernen
removeImage.removeImage=Bild entfernen
removeImage.submit=Bild entfernen
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_el_GR.properties b/src/main/resources/messages_el_GR.properties
index b19770e0e9b..a973715e866 100644
--- a/src/main/resources/messages_el_GR.properties
+++ b/src/main/resources/messages_el_GR.properties
@@ -76,6 +76,7 @@ donate=Δωρισε
color=Χρώμα
sponsor=Yποστηρικτής
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Λήψη
pipelineOptions.validateButton=Επικυρώνω
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Αυτόματο Μαύρισμα Κειμένου
@@ -1154,6 +1181,8 @@ licenses.license=Άδεια
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties
index dbebccc0154..29f40d8e727 100644
--- a/src/main/resources/messages_en_GB.properties
+++ b/src/main/resources/messages_en_GB.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed to import file
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of a page and/or its contents.
scalePages.tags=resize,modify,dimension,adapt
-home.pipeline.title=Pipeline (Advanced)
+home.pipeline.title=Pipeline
home.pipeline.desc=Run multiple actions on PDFs by defining pipeline scripts
pipeline.tags=automate,sequence,scripted,batch-process
@@ -482,10 +502,15 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
-replace-color.title=Replace-Invert-Color
+replace-color.title=Advanced Colour options
replace-color.header=Replace-Invert Color PDF
-home.replaceColorPdf.title=Replace and Invert Color
+home.replaceColorPdf.title=Advanced Colour options
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
replace-color.selectText.1=Replace or Invert color Options
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Redact
@@ -1154,7 +1181,9 @@ licenses.license=Licence
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
-survey.please=Please consider taking our survey!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
+survey.please=Please consider taking our survey to have input on the future of Stirling-PDF!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
survey.dontShowAgain=Don't show again
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties
index 6ea5dd95996..eadf3b031c5 100644
--- a/src/main/resources/messages_en_US.properties
+++ b/src/main/resources/messages_en_US.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Redact
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_es_ES.properties b/src/main/resources/messages_es_ES.properties
index 5274fc1d2e2..624ef96b241 100644
--- a/src/main/resources/messages_es_ES.properties
+++ b/src/main/resources/messages_es_ES.properties
@@ -76,6 +76,7 @@ donate=Donar
color=Color
sponsor=Patrocinador
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Canalización:
pipelineOptions.saveButton=Descargar
pipelineOptions.validateButton=Validar
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convertir desde PDF
navbar.sections.security=Señalización y seguridad
navbar.sections.advance=Avanzado
navbar.sections.edit=Ver y Editar
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Archivo no encontrado
database.fileNullOrEmpty=El archivo no debe ser nulo o vacío.
database.failedImportFile=Archivo de importación fallido
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Escalar/ajustar tamaño de página
home.scalePages.desc=Escalar/cambiar el tamaño de una pagina y/o su contenido
scalePages.tags=cambiar tamaño,modificar,dimensionar,adaptar
-home.pipeline.title=Secuencia (Avanzado)
+home.pipeline.title=Secuencia
home.pipeline.desc=Ejecutar varias tareas a PDFs definiendo una secuencia de comandos
pipeline.tags=automatizar,secuencia,con script,proceso por lotes
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Eliminar imagen
home.removeImagePdf.desc=Eliminar imagen del PDF> para reducir el tamaño de archivo
removeImagePdf.tags=Eliminar imagen,Operaciones de página,Back end,lado del servidor
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Acceso denegado
login.oauth2InvalidTokenResponse=Respuesta de token no válida
login.oauth2InvalidIdToken=Token de identificación no válido
login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Redactar
@@ -1154,6 +1181,8 @@ licenses.license=Licencia
survey.nav=Encuesta
survey.title=Encuesta Stirling-PDF
survey.description=Stirling-PDF no tiene seguimiento, por lo que queremos escuchar a nuestros usuarios para mejorar Stirling-PDF.
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=¡Considere realizar nuestra encuesta!
survey.disabled=(La ventana emergente de la encuesta se desactivará en las siguientes actualizaciones, pero estará disponible al pie de la página.)
survey.button=Realizar encuesta
@@ -1179,3 +1208,17 @@ removeImage.title=Eliminar imagen
removeImage.header=Eliminar imagen
removeImage.removeImage=Eliminar imagen
removeImage.submit=Eliminar imagen
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_eu_ES.properties b/src/main/resources/messages_eu_ES.properties
index 792955c3262..99a6a153149 100644
--- a/src/main/resources/messages_eu_ES.properties
+++ b/src/main/resources/messages_eu_ES.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Idatzi
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_fr_FR.properties b/src/main/resources/messages_fr_FR.properties
index ee00bb486ad..aca42d2b21b 100644
--- a/src/main/resources/messages_fr_FR.properties
+++ b/src/main/resources/messages_fr_FR.properties
@@ -76,6 +76,7 @@ donate=Faire un don
color=Couleur
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Télécharger
pipelineOptions.validateButton=Valider
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convertir depuis PDF
navbar.sections.security=Signature et sécurité
navbar.sections.advance=Mode avancé
navbar.sections.edit=Voir et modifier
+navbar.sections.popular=Populaire
#############
# SETTINGS #
@@ -184,9 +202,9 @@ adminUserSettings.header=Administration des paramètres des utilisateurs
adminUserSettings.admin=Administateur
adminUserSettings.user=Utilisateur
adminUserSettings.addUser=Ajouter un utilisateur
-adminUserSettings.deleteUser=Delete User
-adminUserSettings.confirmDeleteUser=Should the user be deleted?
-adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
+adminUserSettings.deleteUser=Supprimer l'utilisateur
+adminUserSettings.confirmDeleteUser=Voulez vous vraiment supprimer l'utilisateur ?
+adminUserSettings.confirmChangeUserStatus=Voulez vous vraiment déactiver/réactiver l'utilisateur ?
adminUserSettings.usernameInfo=Le nom d'utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
adminUserSettings.roles=Rôles
adminUserSettings.role=Rôle
@@ -200,13 +218,13 @@ adminUserSettings.forceChange=Forcer l’utilisateur à changer son nom d’util
adminUserSettings.submit=Ajouter
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
adminUserSettings.authenticated=Authentifié
-adminUserSettings.editOwnProfil=Edit own profile
-adminUserSettings.enabledUser=enabled user
-adminUserSettings.disabledUser=disabled user
-adminUserSettings.activeUsers=Active Users:
-adminUserSettings.disabledUsers=Disabled Users:
-adminUserSettings.totalUsers=Total Users:
-adminUserSettings.lastRequest=Last Request
+adminUserSettings.editOwnProfil=Éditer son propre profil
+adminUserSettings.enabledUser=Utilisateur activé
+adminUserSettings.disabledUser=Utilisateur désactivé
+adminUserSettings.activeUsers=Utilisateurs actifs :
+adminUserSettings.disabledUsers=Utilisateurs désactivés :
+adminUserSettings.totalUsers=Utilisateurs au total :
+adminUserSettings.lastRequest=Dernière requête
database.title=Database Import/Export
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Votre session a expiré. Veuillez recharger la page et réessayer.
+
#############
# HOME-PAGE #
#############
@@ -301,7 +321,7 @@ home.changeMetadata.desc=Modifiez, supprimez ou ajoutez des métadonnées à un
changeMetadata.tags=métadonnées,titre,auteur,date,création,heure,éditeur,statistiques,title,author,date,creation,time,publisher,producer,stats,metadata
home.fileToPDF.title=Fichier en PDF
-home.fileToPDF.desc=Convertissez presque n’importe quel fichiers en PDF (DOCX, PNG, XLS, PPT, TXT et plus).
+home.fileToPDF.desc=Convertissez presque n’importe quel fichier en PDF (DOCX, PNG, XLS, PPT, TXT, etc.).
fileToPDF.tags=convertion,transformation,format,document,image,slide,texte,conversion,office,docs,word,excel,powerpoint
home.ocr.title=OCR / Nettoyage des numérisations
@@ -370,9 +390,9 @@ home.certSign.title=Signer avec un certificat
home.certSign.desc=Signez un PDF avec un certificat ou une clé (PEM/P12).
certSign.tags=signer,chiffrer,certificat,authenticate,PEM,P12,official,encrypt
-home.removeCertSign.title=Remove Certificate Sign
-home.removeCertSign.desc=Remove certificate signature from PDF
-removeCertSign.tags=authenticate,PEM,P12,official,decrypt
+home.removeCertSign.title=Supprimer la signature par certificat
+home.removeCertSign.desc=Supprimez la signature par certificat d'un PDF
+removeCertSign.tags=signer,chiffrer,certificat,authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Fusionner des pages
home.pageLayout.desc=Fusionnez plusieurs pages d’un PDF en une seule.
@@ -382,7 +402,7 @@ home.scalePages.title=Ajuster l’échelle ou la taille
home.scalePages.desc=Modifiez la taille ou l’échelle d’une page et/ou de son contenu.
scalePages.tags=ajuster,redimensionner,resize,modify,dimension,adapt
-home.pipeline.title=Pipeline (avancé)
+home.pipeline.title=Pipeline
home.pipeline.desc=Exécutez plusieurs actions sur les PDF en définissant des scripts de pipeline.
pipeline.tags=automatiser,séquencer,automate,sequence,scripted,batch-process
@@ -478,9 +498,14 @@ home.BookToPDF.title=eBook vers PDF
home.BookToPDF.desc=Convertit les formats de livres/bandes dessinées en PDF à l'aide de calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
-home.removeImagePdf.title=Remove image
-home.removeImagePdf.desc=Remove image from PDF to reduce file size
-removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+home.removeImagePdf.title=Supprimer les images
+home.removeImagePdf.desc=Supprimez les images d'un PDF pour réduire sa taille
+removeImagePdf.tags=Images,Remove Image,Page operations,Back end,server side
+
+
+home.splitPdfByChapters.title=Séparer un PDF par chapitres
+home.splitPdfByChapters.desc=Séparez un PDF en fichiers multiples en fonction de sa structure par chapitres.
+splitPdfByChapters.tags=séparer,chapitres,split,chapters,bookmarks,organize
#replace-invert-color
replace-color.title=Replace-Invert-Color
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Accès refusé
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
login.oauth2InvalidIdToken=Jeton d'identification invalide
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Caviarder automatiquement
@@ -759,14 +786,14 @@ compare.submit=Comparer
#BookToPDF
BookToPDF.title=Livres et BD vers PDF
BookToPDF.header=Livre vers PDF
-BookToPDF.credit=Utiliser Calibre
+BookToPDF.credit=Utilise Calibre
BookToPDF.submit=Convertir
#PDFToBook
PDFToBook.title=PDF vers Livre
PDFToBook.header=PDF vers Livre
PDFToBook.selectText.1=Format
-PDFToBook.credit=Utiliser Calibre
+PDFToBook.credit=Utilise Calibre
PDFToBook.submit=Convertir
#sign
@@ -1049,7 +1076,7 @@ pdfToPDFA.credit=Ce service utilise ghostscript pour la conversion en PDF/A.
pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Ne fonctionne actuellement pas pour plusieurs entrées à la fois
pdfToPDFA.outputFormat=Format de sortie
-pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
+pdfToPDFA.pdfWithDigitalSignature=Le PDF contient une signature numérique. Elle sera supprimée dans l'étape suivante.
#PDFToWord
@@ -1154,6 +1181,8 @@ licenses.license=Licence
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_ga_IE.properties b/src/main/resources/messages_ga_IE.properties
index 2360538a952..c5389eb40c3 100644
--- a/src/main/resources/messages_ga_IE.properties
+++ b/src/main/resources/messages_ga_IE.properties
@@ -76,6 +76,7 @@ donate=Síntiúis
color=Dath
sponsor=Urraitheoir
info=Eolas
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Píblíne:
pipelineOptions.saveButton=Íosluchtaigh
pipelineOptions.validateButton=Bailíochtaigh
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Tiontaigh ó PDF
navbar.sections.security=Comhartha & Slándáil
navbar.sections.advance=Casta
navbar.sections.edit=Féach ar & Cuir in Eagar
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Comhad gan aimsiú
database.fileNullOrEmpty=Níor cheart go mbeadh an comhad ar neamhní nó folamh
database.failedImportFile=Theip ar iompórtáil an chomhaid
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Rochtain Diúltaithe
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Redact
@@ -1154,6 +1181,8 @@ licenses.license=Ceadúnas
survey.nav=Suirbhé
survey.title=Suirbhé Stirling-PDF
survey.description=Níl aon rian ar Stirling-PDF agus mar sin ba mhaith linn cloisteáil ónár n-úsáideoirí chun feabhas a chur ar Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Smaoinigh ar ár suirbhé a dhéanamh le do thoil!
survey.disabled=(Díchumasófar aníos an tsuirbhé sna nuashonruithe seo a leanas ach beidh siad ar fáil ag bun an leathanaigh)
survey.button=Tóg Suirbhé
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_hi_IN.properties b/src/main/resources/messages_hi_IN.properties
index d93dc2ddec1..93983531843 100644
--- a/src/main/resources/messages_hi_IN.properties
+++ b/src/main/resources/messages_hi_IN.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=पृष्ठ
pages=पृष्ठों
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=पीडीएफ से कनवर्ट कर
navbar.sections.security=संकेत और सुरक्षा
navbar.sections.advance=उन्नत
navbar.sections.edit=देखें और संपादित करें
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=स्वत: गोपनीयकरण
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_hr_HR.properties b/src/main/resources/messages_hr_HR.properties
index e8f93b20015..aa0cd65cccd 100644
--- a/src/main/resources/messages_hr_HR.properties
+++ b/src/main/resources/messages_hr_HR.properties
@@ -76,6 +76,7 @@ donate=Doniraj
color=Boja
sponsor=Sponzor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Preuzmi datoteku
pipelineOptions.validateButton=Potvrdi
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Pretvori iz PDF
navbar.sections.security=Potpis & sigurnost
navbar.sections.advance=Napredno
navbar.sections.edit=Pregled & Uređivanje
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Prilagodite veličinu/razmjer stranice
home.scalePages.desc=Promijenite veličinu/razmjer stranice i/ili njezin sadržaj.
scalePages.tags=izmjena,modifikacija,dimenzija,adaptacija
-home.pipeline.title=Pipeline (Advanced)
+home.pipeline.title=Pipeline
home.pipeline.desc=Izvršite više radnji na PDF-ovima definiranjem skripti u pipeline-u
pipeline.tags=automatizacija,sekvenciranje,skriptirano,batch-process
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Pristup odbijen
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
login.oauth2InvalidIdToken=Nevažeći ID token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Automatsko uređivanje
@@ -1154,6 +1181,8 @@ licenses.license=Licenca
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_hu_HU.properties b/src/main/resources/messages_hu_HU.properties
index e22cee08c4d..c53e52e1d83 100644
--- a/src/main/resources/messages_hu_HU.properties
+++ b/src/main/resources/messages_hu_HU.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Érzékeny tartalom eltávolítása
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_id_ID.properties b/src/main/resources/messages_id_ID.properties
index cbc0e61a196..afdea33e69b 100644
--- a/src/main/resources/messages_id_ID.properties
+++ b/src/main/resources/messages_id_ID.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Validate
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Menyesuaikan ukuran/skala halaman
home.scalePages.desc=Mengubah ukuran/skala halaman dan/atau isinya.
scalePages.tags=mengubah ukuran, memodifikasi, dimensi, mengadaptasi
-home.pipeline.title=Pipeline (Lanjutan)
+home.pipeline.title=Pipeline
home.pipeline.desc=Menjalankan beberapa tindakan pada PDF dengan mendefinisikan skrip pipeline
pipeline.tags=mengotomatiskan, mengurutkan, menulis, proses batch
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Redaksional Otomatis
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_it_IT.properties b/src/main/resources/messages_it_IT.properties
index e8fb9fcca38..bc3de60ba05 100644
--- a/src/main/resources/messages_it_IT.properties
+++ b/src/main/resources/messages_it_IT.properties
@@ -76,8 +76,9 @@ donate=Donazione
color=Colore
sponsor=Sponsor
info=Info
-page=Page
-pages=Pages
+pro=Pro
+page=Pagina
+pages=Pagine
legal.privacy=Informativa sulla privacy
legal.terms=Termini e Condizioni
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Download
pipelineOptions.validateButton=Convalidare
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Aggiorna alla versione Pro
+enterpriseEdition.warning=Questa funzionalità è disponibile solo per gli utenti Pro.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supporta i file di configurazione YAML e altre funzionalità SSO.
+enterpriseEdition.ssoAdvert=Cerchi altre funzionalità di gestione degli utenti? Dai un'occhiata a Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Vuoi migliorare Stirling PDF?
+analytics.paragraph1=Stirling PDF ha opt-in analytics per aiutarci a migliorare il prodotto. Non tracciamo alcuna informazione personale o contenuto di file.
+analytics.paragraph2=Si prega di prendere in considerazione l'attivazione dell'analytics per aiutare Stirling-PDF a crescere e consentirci di comprendere meglio i nostri utenti.
+analytics.enable=Abilita analytics
+analytics.disable=Disabilita analytics
+analytics.settings=È possibile modificare le impostazioni per analitycs nel file config/settings.yml
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Converti da PDF
navbar.sections.security=Firma & Sicurezza
navbar.sections.advance=Avanzate
navbar.sections.edit=Visualizza & Modifica
+navbar.sections.popular=Populare
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File non trovato
database.fileNullOrEmpty=Il file non deve essere nullo o vuoto
database.failedImportFile=Importazione file non riuscita
+session.expired=La tua sessione è scaduta. Aggiorna la pagina e riprova.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Regola le dimensioni/scala della pagina
home.scalePages.desc=Modificare le dimensioni/scala della pagina e/o dei suoi contenuti.
scalePages.tags=ridimensionare,modificare,dimensionare,adattare
-home.pipeline.title=Pipeline (avanzato)
+home.pipeline.title=Pipeline
home.pipeline.desc=Esegui più azioni sui PDF definendo script di pipeline
pipeline.tags=automatizzare,sequenziare,scriptare,elaborare in batch
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Rimuovi immagine
home.removeImagePdf.desc=Rimuovi le immagini dal PDF per ridurre la dimensione del file
removeImagePdf.tags=Rimuovi immagine,operazioni sulla pagina,back-end,lato server
+
+home.splitPdfByChapters.title=Dividi PDF per capitoli
+home.splitPdfByChapters.desc=Dividi un PDF in più file in base alla struttura dei capitoli.
+splitPdfByChapters.tags=dividi, capitoli, segnalibri, organizza
+
#replace-invert-color
replace-color.title=Sostituisci-Inverti-Colore
replace-color.header=Sostituisci-Inverti colore PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Accesso negato
login.oauth2InvalidTokenResponse=Risposta token non valida
login.oauth2InvalidIdToken=Id Token non valido
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
-
+login.alreadyLoggedIn=Hai già effettuato l'accesso a
+login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova.
+login.toManySessions=Hai troppe sessioni attive
#auto-redact
autoRedact.title=Redazione automatica
@@ -1154,6 +1181,8 @@ licenses.license=Licenza
survey.nav=Sondaggio
survey.title=Sondaggio Stirling-PDF
survey.description=Stirling-PDF non fa tracciamento, quindi vogliamo sentire i nostri utenti per migliorare Stirling-PDF!
+survey.changes=Stirling-PDF è cambiato dall'ultimo sondaggio! Per saperne di più, consulta il nostro blog qui:
+survey.changes2=Con questi cambiamenti stiamo ricevendo supporto aziendale e finanziamenti retribuiti
survey.please=Ti invitiamo a prendere in considerazione la possibilità di partecipare al nostro sondaggio!
survey.disabled=(Il popup del sondaggio verrà disabilitato nei prossimi aggiornamenti ma sarà disponibile a piè di pagina)
survey.button=Partecipa al sondaggio
@@ -1179,3 +1208,17 @@ removeImage.title=Rimuovere immagine
removeImage.header=Rimuovi immagine
removeImage.removeImage=Rimuovi immagine
removeImage.submit=Rimuovi immagine
+
+
+splitByChapters.title=Dividere PDF per capitoli
+splitByChapters.header=Dividi PDF per capitoli
+splitByChapters.bookmarkLevel=Livello segnalibro
+splitByChapters.includeMetadata=Includi Metadati
+splitByChapters.allowDuplicates=Consenti duplicati
+splitByChapters.desc.1=Questo strumento divide un file PDF in più PDF in base alla struttura dei capitoli.
+splitByChapters.desc.2=Livello segnalibro: seleziona il livello dei segnalibri da utilizzare per la suddivisione (0 per il livello superiore, 1 per il secondo livello, ecc.).
+splitByChapters.desc.3=Includi metadati: se selezionato, i metadati del PDF originale verranno inclusi in ogni PDF diviso.
+splitByChapters.desc.4=Consenti duplicati: se selezionata, consente più segnalibri sulla stessa pagina per creare PDF separati.
+splitByChapters.submit=Dividi PDF
+
+
diff --git a/src/main/resources/messages_ja_JP.properties b/src/main/resources/messages_ja_JP.properties
index e5286295d3c..6e7aac2c03f 100644
--- a/src/main/resources/messages_ja_JP.properties
+++ b/src/main/resources/messages_ja_JP.properties
@@ -76,6 +76,7 @@ donate=寄付する
color=色
sponsor=スポンサー
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=パイプライン:
pipelineOptions.saveButton=ダウンロード
pipelineOptions.validateButton=検証
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=PDFから変換
navbar.sections.security=署名とセキュリティ
navbar.sections.advance=アドバンスド
navbar.sections.edit=閲覧と編集
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=ファイルが見つかりません
database.fileNullOrEmpty=ファイルは null または空であってはなりません
database.failedImportFile=ファイルのインポートに失敗
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=ページの縮尺の調整
home.scalePages.desc=ページやコンテンツの縮尺を変更します。
scalePages.tags=resize,modify,dimension,adapt
-home.pipeline.title=パイプライン (高度)
+home.pipeline.title=パイプライン
home.pipeline.desc=パイプラインスクリプトを定義してPDF上で複数のアクションを実行します。
pipeline.tags=automate,sequence,scripted,batch-process
@@ -482,6 +502,11 @@ home.removeImagePdf.title=画像の削除
home.removeImagePdf.desc=PDFから画像を削除してファイルサイズを小さくします
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=アクセス拒否
login.oauth2InvalidTokenResponse=無効なトークン応答
login.oauth2InvalidIdToken=無効なIDトークン
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=自動塗りつぶし
@@ -1154,6 +1181,8 @@ licenses.license=ライセンス
survey.nav=アンケート
survey.title=Stirling-PDFのアンケート
survey.description=Stirling-PDFには追跡機能がないため、Stirling-PDFをより良くするために皆様の意見を聞かせてください!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=アンケートにご協力ください!
survey.disabled=(アンケートのポップアップは、次の更新では無効になりますが、ページの下部に表示されます。)
survey.button=アンケートに答える
@@ -1179,3 +1208,17 @@ removeImage.title=画像の削除
removeImage.header=画像の削除
removeImage.removeImage=画像の削除
removeImage.submit=画像を削除
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_ko_KR.properties b/src/main/resources/messages_ko_KR.properties
index 43d8f84ec00..832f7affb6e 100644
--- a/src/main/resources/messages_ko_KR.properties
+++ b/src/main/resources/messages_ko_KR.properties
@@ -76,6 +76,7 @@ donate=기부하기
color=색상
sponsor=스폰서
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=파이프라인:
pipelineOptions.saveButton=다운로드
pipelineOptions.validateButton=확인
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=자동 검열
@@ -1154,6 +1181,8 @@ licenses.license=라이센스
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_nl_NL.properties b/src/main/resources/messages_nl_NL.properties
index 4d1469d8bc8..ed8096c3ba9 100644
--- a/src/main/resources/messages_nl_NL.properties
+++ b/src/main/resources/messages_nl_NL.properties
@@ -76,6 +76,7 @@ donate=Doneer
color=Kleur
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pijplijn:
pipelineOptions.saveButton=Downloaden
pipelineOptions.validateButton=Valideren
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Converteren van PDF
navbar.sections.security=Ondertekenen & beveiliging
navbar.sections.advance=Geavanceerd
navbar.sections.edit=Bekijken & wijzigen
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Aanpassen paginaformaat/schaal
home.scalePages.desc=Wijzig de grootte/schaal van een pagina en/of de inhoud ervan.
scalePages.tags=resize,aanpassen,dimensie,aanpassen
-home.pipeline.title=Pijplijn (Geavanceerd)
+home.pipeline.title=Pijplijn
home.pipeline.desc=Voer meerdere acties uit op PDF's door pipelinescripts te definiëren
pipeline.tags=automatiseren,volgorde,gescrript,batch-verwerking
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Toegang geweigerd
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
login.oauth2InvalidIdToken=Ongeldige ID token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Automatisch censureren
@@ -1154,6 +1181,8 @@ licenses.license=Licentie
survey.nav=Enquête
survey.title=Stirling-PDF Enquête
survey.description=Stirling-PDF heeft geen tracking, dus we willen van onze gebruikers horen om Stirling-PDF te verbeteren.
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Overweeg alstublieft om onze enquête in te vullen!
survey.disabled=(Enquête popup wordt in een toekomstige update weggehaald, maar is beschikbaar aan de onderkant van de pagina.)
survey.button=Vul enquête in.
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_no_NB.properties b/src/main/resources/messages_no_NB.properties
index abd0738a3a2..207ab27dc20 100644
--- a/src/main/resources/messages_no_NB.properties
+++ b/src/main/resources/messages_no_NB.properties
@@ -76,6 +76,7 @@ donate=Doner
color=Farge
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Last ned
pipelineOptions.validateButton=Valider
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Konverter fra PDF
navbar.sections.security=Signer & Sikkerhet
navbar.sections.advance=Avansert
navbar.sections.edit=Vis & Rediger
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Fil ikke funnet
database.fileNullOrEmpty=Fil må ikke være tom eller null
database.failedImportFile=Import av fil mislyktes
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Tilgang nektet
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
login.oauth2InvalidIdToken=Ugyldig Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Automatisk Sensurering
@@ -1154,6 +1181,8 @@ licenses.license=Lisens
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_pl_PL.properties b/src/main/resources/messages_pl_PL.properties
index c15170cbdcd..241ae94c963 100755
--- a/src/main/resources/messages_pl_PL.properties
+++ b/src/main/resources/messages_pl_PL.properties
@@ -3,8 +3,8 @@
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
-addPageNumbers.fontSize=Font Size
-addPageNumbers.fontName=Font Name
+addPageNumbers.fontSize=Rozmiar Czcionki
+addPageNumbers.fontName=Nazwa Czcionki
pdfPrompt=Wybierz PDF
multiPdfPrompt=Wybierz PDF (2+)
multiPdfDropPrompt=Wybierz (lub przeciągnij i puść) wszystkie dokumenty PDF
@@ -56,12 +56,12 @@ userNotFoundMessage=Brak użytkownika.
incorrectPasswordMessage=Nieprawidłowe hasło.
usernameExistsMessage=Taki uzytkownik już istnieje.
invalidUsernameMessage=Niewłaściwa nazwa użytkownika - musi zawierać litery, cyfry i @._+- LUB być adresem email.
-invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
+invalidPasswordMessage=Hasło nie może być puste i nie może zawierać spacji na początku ani na końcu.
confirmPasswordErrorMessage=Wpisz poprawnie hasło w OBA pola.
deleteCurrentUserMessage=Nie można usunąć zalogowanego użytkownika
deleteUsernameExistsMessage=Nie można usunąć zalogowanego użytkownika
downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika
-disabledCurrentUserMessage=The current user cannot be disabled
+disabledCurrentUserMessage=Nie można wyłączyć bieżącego użytkownika
downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony.
userAlreadyExistsOAuthMessage=Takie konto użytkownika istnieje - stworzone za pomocą OAuth2.
userAlreadyExistsWebMessage=Takie konto użytkownika istnieje - stworzone za pomocą przeglądarki.
@@ -76,14 +76,15 @@ donate=Podaruj
color=kolor
sponsor=sponsor
info=informacje
-page=Page
-pages=Pages
+pro=Pro
+page=Strona
+pages=Strony
-legal.privacy=Privacy Policy
-legal.terms=Terms and Conditions
-legal.accessibility=Accessibility
-legal.cookie=Cookie Policy
-legal.impressum=Impressum
+legal.privacy=Polityka Prywatności
+legal.terms=Zasady i Postanowienia
+legal.accessibility=Dostępność
+legal.cookie=Polityka plików cookie
+legal.impressum=Impresja
###############
# Pipeline #
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Automatyzacja
pipelineOptions.saveButton=Pobierz
pipelineOptions.validateButton=Waliduj
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Uaktualnij do wersji Pro
+enterpriseEdition.warning=Ta funkcja jest dostępna tylko dla użytkowników Pro.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro obsługuje pliki konfiguracyjne YAML i inne funkcje SSO.
+enterpriseEdition.ssoAdvert=Szukasz więcej funkcji zarządzania użytkownikami? Sprawdź Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Czy chcesz ulepszyć Stirling PDF?
+analytics.paragraph1=Stirling PDF ma opcję analizy, która pomaga nam udoskonalać produkt. Nie śledzimy żadnych danych osobowych ani zawartości plików.
+analytics.paragraph2=Rozważ włączenie funkcji analitycznych, które pomogą w rozwoju Stirling-PDF i pozwolą nam lepiej zrozumieć naszych użytkowników.
+analytics.enable=Włącz analitykę
+analytics.disable=Wyłącz analitykę
+analytics.settings=Możesz zmienić ustawienia analityki w pliku config/settings.yml
#############
# NAVBAR #
@@ -121,13 +138,14 @@ navbar.darkmode=Tryb nocny
navbar.language=Języki
navbar.settings=Ustawienia
navbar.allTools=Narzędzia
-navbar.multiTool=Multi Tools
+navbar.multiTool=Narzędzie Wielofunkcyjne
navbar.sections.organize=Organizuj
navbar.sections.convertTo=Przetwórz na PDF
navbar.sections.convertFrom=Przetwórz z PDF
navbar.sections.security=Podpis i bezpieczeństwo
navbar.sections.advance=Zaawansowane
navbar.sections.edit=Podgląd i edycja
+navbar.sections.popular=Popularne
#############
# SETTINGS #
@@ -200,13 +218,13 @@ adminUserSettings.forceChange=Wymuś zmianę hasło po zalogowaniu
adminUserSettings.submit=Zapisz użytkownika
adminUserSettings.changeUserRole=Zmień rolę użytkownika
adminUserSettings.authenticated=Zalogowany
-adminUserSettings.editOwnProfil=Edit own profile
-adminUserSettings.enabledUser=enabled user
-adminUserSettings.disabledUser=disabled user
-adminUserSettings.activeUsers=Active Users:
-adminUserSettings.disabledUsers=Disabled Users:
-adminUserSettings.totalUsers=Total Users:
-adminUserSettings.lastRequest=Last Request
+adminUserSettings.editOwnProfil=Edytuj własny profil
+adminUserSettings.enabledUser=włączony użytkownik
+adminUserSettings.disabledUser=wyłączony użytkownik
+adminUserSettings.activeUsers=Aktywni Użytkownicy:
+adminUserSettings.disabledUsers=Wyłączeni Użytkownicy:
+adminUserSettings.totalUsers=Łączna Liczba Użytkowników:
+adminUserSettings.lastRequest=Ostatnie Zgłoszenie
database.title=Import/Eksport bazy danych
@@ -225,6 +243,8 @@ database.fileNotFound=Plik nie znaleziony
database.fileNullOrEmpty=Plik nie może być pusty
database.failedImportFile=Nie udało się zaimportować pliku
+session.expired=Twoja sesja wygasła. Odśwież stronę i spróbuj ponownie.
+
#############
# HOME-PAGE #
#############
@@ -236,207 +256,207 @@ home.viewPdf.title=Podejrzyj PDF
home.viewPdf.desc=Wyświetl, adnotuj, dodaj tekst lub obrazy
viewPdf.tags=wyświetl,czytaj,adnotuj,tekst,obraz
-home.multiTool.title=Multi narzędzie PDF
+home.multiTool.title=Wielofunkcyjne Narzędzie PDF
home.multiTool.desc=Łącz, dziel, obracaj, zmieniaj kolejność i usuwaj strony
-multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side
+multiTool.tags=Wielofunkcyjne narzędzie, obsługa wielu operacji, interfejs użytkownika, przeciąganie kliknięć, front-end, strona klienta
home.merge.title=Połącz
home.merge.desc=Łatwe łączenie wielu dokumentów PDF w jeden.
-merge.tags=merge,Page operations,Back end,server side
+merge.tags=scalanie, operacje na stronach, back-end, po stronie serwera
home.split.title=Podziel
home.split.desc=Podziel dokument PDF na wiele dokumentów
-split.tags=Page operations,divide,Multi Page,cut,server side
+split.tags=Operacje na stronach, dzielenie, wiele stron, cięcie, po stronie serwera
home.rotate.title=Obróć
home.rotate.desc=Łatwo obracaj dokumenty PDF.
-rotate.tags=server side
+rotate.tags=strona serwera
home.imageToPdf.title=Obraz na PDF
home.imageToPdf.desc=Konwertuj obraz (PNG, JPEG, GIF) do dokumentu PDF.
-imageToPdf.tags=conversion,img,jpg,picture,photo
+imageToPdf.tags=konwersja,img,jpg,obraz,zdjęcie
home.pdfToImage.title=PDF na Obraz
home.pdfToImage.desc=Konwertuj plik PDF na obraz (PNG, JPEG, GIF).
-pdfToImage.tags=conversion,img,jpg,picture,photo
+pdfToImage.tags=konwersja,img,jpg,obraz,zdjęcie
home.pdfOrganiser.title=Uporządkuj
home.pdfOrganiser.desc=Usuń/Zmień kolejność stron w dowolnej kolejności
-pdfOrganiser.tags=duplex,even,odd,sort,move
+pdfOrganiser.tags=duplex,parzyste,nieparzyste,sortuj,przenieś
home.addImage.title=Dodaj obraz
home.addImage.desc=Dodaje obraz w wybranym miejscu w dokumencie PDF
-addImage.tags=img,jpg,picture,photo
+addImage.tags=img,jpg,obraz,zdjęcie
home.watermark.title=Dodaj znak wodny
home.watermark.desc=Dodaj niestandardowy znak wodny do dokumentu PDF.
-watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo
+watermark.tags=Tekst,powtarzanie,etykieta,własne,prawa autorskie,znak wodny,img,jpg,obraz,zdjęcie
home.permissions.title=Zmień uprawnienia
home.permissions.desc=Zmień uprawnienia dokumentu PDF
-permissions.tags=read,write,edit,print
+permissions.tags=odczyt,zapis,edycja,drukowanie
home.removePages.title=Usuń
home.removePages.desc=Usuń niechciane strony z dokumentu PDF.
-removePages.tags=Remove pages,delete pages
+removePages.tags=Usuń strony,usuwaj strony
home.addPassword.title=Dodaj hasło
home.addPassword.desc=Zaszyfruj dokument PDF za pomocą hasła.
-addPassword.tags=secure,security
+addPassword.tags=bezpieczeństwo,ochrona
home.removePassword.title=Usuń hasło
home.removePassword.desc=Usuń ochronę hasłem z dokumentu PDF.
-removePassword.tags=secure,Decrypt,security,unpassword,delete password
+removePassword.tags=zabezpieczenie,odszyfrowanie,bezpieczeństwo,odhasłowanie,usunięcie hasła
home.compressPdfs.title=Kompresuj
home.compressPdfs.desc=Kompresuj dokumenty PDF, aby zmniejszyć ich rozmiar.
-compressPdfs.tags=squish,small,tiny
+compressPdfs.tags=zgniatać,mały,malutki
home.changeMetadata.title=Zmień metadane
home.changeMetadata.desc=Zmień/Usuń/Dodaj metadane w dokumencie PDF
-changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
+changeMetadata.tags=Tytuł,autor,data,utworzenie,czas,wydawca,producent,statystyki
home.fileToPDF.title=Konwertuj plik do PDF
home.fileToPDF.desc=Konwertuj dowolny plik do dokumentu PDF (DOCX, PNG, XLS, PPT, TXT i więcej)
-fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint
+fileToPDF.tags=transformacja,format,dokument,obraz,slajd,tekst,konwersja,office,dokumenty,word,excel,powerpoint
home.ocr.title=OCR / Zamiana na tekst
home.ocr.desc=OCR skanuje i wykrywa tekst z obrazów w dokumencie PDF i zamienia go na tekst.
-ocr.tags=recognition,text,image,scan,read,identify,detection,editable
+ocr.tags=rozpoznawanie, tekst, obraz, skanowanie, odczyt, identyfikacja, wykrywanie, edytowalność
home.extractImages.title=Wyodrębnij obrazy
home.extractImages.desc=Wyodrębnia wszystkie obrazy z dokumentu PDF i zapisuje je w wybranym formacie
-extractImages.tags=picture,photo,save,archive,zip,capture,grab
+extractImages.tags=obraz, zdjęcie, zapisz, archiwum, zip, przechwyć, złap
home.pdfToPDFA.title=PDF na PDF/A
home.pdfToPDFA.desc=Konwertuj dokument PDF na PDF/A w celu długoterminowego przechowywania
-pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation
+pdfToPDFA.tags=archiwum, długoterminowe, standardowe, konwersja, przechowywanie, konserwacja
home.PDFToWord.title=PDF na Word
home.PDFToWord.desc=Konwertuj dokument PDF na formaty Word (DOC, DOCX i ODT)
-PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile
+PDFToWord.tags=doc,docx,odt,word, przekształcenie, transformacja, konwersja, office, microsoft, plik doc
home.PDFToPresentation.title=PDF na Prezentację
home.PDFToPresentation.desc=Konwertuj dokument PDF na formaty prezentacji (PPT, PPTX i ODP)
-PDFToPresentation.tags=slides,show,office,microsoft
+PDFToPresentation.tags=slajdy, pokaz, office, microsoft
home.PDFToText.title=PDF na Tekst/RTF
home.PDFToText.desc=Konwertuj dokument PDF na tekst lub format RTF
-PDFToText.tags=richformat,richtextformat,rich text format
+PDFToText.tags=format tekstu sformatowanego,rtf format
home.PDFToHTML.title=PDF na HTML
home.PDFToHTML.desc=Konwertuj dokument PDF na format HTML
-PDFToHTML.tags=web content,browser friendly
+PDFToHTML.tags=zawartość internetowa, przyjazne dla przeglądarek
home.PDFToXML.title=PDF na XML
home.PDFToXML.desc=Konwertuj dokument PDF na format XML
-PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert
+PDFToXML.tags=ekstrakcja danych, zawartość strukturalna, współdziałanie, transformacja, konwertowanie
home.ScannerImageSplit.title=Wykryj/Podziel zeskanowane zdjęcia
home.ScannerImageSplit.desc=Podziel na wiele zdjęć z jednego zdjęcia/PDF
-ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize
+ScannerImageSplit.tags=oddzielne, automatyczne wykrywanie, skanowanie, wiele zdjęć, porządkowanie
home.sign.title=Podpis
home.sign.desc=Dodaje podpis do dokumentu PDF za pomocą rysunku, tekstu lub obrazu
-sign.tags=authorize,initials,drawn-signature,text-sign,image-signature
+sign.tags=autoryzacja, inicjały, podpis odręczny, podpis tekstowy, podpis graficzny
home.flatten.title=Spłaszcz
home.flatten.desc=Usuń wszystkie interaktywne elementy i formularze z dokumentu PDF
-flatten.tags=static,deactivate,non-interactive,streamline
+flatten.tags=statyczny, dezaktywacja, nieinteraktywny, opływowy, streamline
home.repair.title=Napraw
home.repair.desc=Spróbuj naprawić uszkodzony dokument PDF
-repair.tags=fix,restore,correction,recover
+repair.tags=naprawianie, naprawa, przywracanie, poprawianie, odzyskiwanie
home.removeBlanks.title=Usuń puste strony
home.removeBlanks.desc=Wykrywa i usuwa puste strony z dokumentu PDF
-removeBlanks.tags=cleanup,streamline,non-content,organize
+removeBlanks.tags=czyszczenie, usprawnianie, brak treści, organizowanie
home.removeAnnotations.title=Usuń notatki/przypisy
home.removeAnnotations.desc=Usuwa wszystkie notatki i przypisy z dokumentu PDF
-removeAnnotations.tags=comments,highlight,notes,markup,remove
+removeAnnotations.tags=komentarze, podświetlanie, notatki, znaczniki, usuwanie
home.compare.title=Porównaj
home.compare.desc=Porównuje i pokazuje różnice między dwoma dokumentami PDF
-compare.tags=differentiate,contrast,changes,analysis
+compare.tags=rozróżnienie, kontrast, zmiany, analiza
home.certSign.title=Podpisz certyfikatem
home.certSign.desc=Podpisz dokument PDF za pomocą certyfikatu/klucza prywatnego (PEM/P12)
-certSign.tags=authenticate,PEM,P12,official,encrypt
+certSign.tags=uwierzytelnianie, PEM, P12, oficjalny, szyfrowanie
home.removeCertSign.title=Usuń podpis certyfikatem
home.removeCertSign.desc=Usuń podpis certyfikatem z dokumentu PDF
-removeCertSign.tags=authenticate,PEM,P12,official,decrypt
+removeCertSign.tags=uwierzytelnianie, PEM, P12, oficjalny, odszyfrowywanie
home.pageLayout.title=Układ wielu stron
home.pageLayout.desc=Scal wiele stron dokumentu PDF w jedną stronę
-pageLayout.tags=merge,composite,single-view,organize
+pageLayout.tags=scalanie, kompozycja, pojedynczy widok, organizowanie, porządkowanie
home.scalePages.title=Dopasuj rozmiar stron
home.scalePages.desc=Dopasuj rozmiar stron wybranego dokumentu PDF
-scalePages.tags=resize,modify,dimension,adapt
+scalePages.tags=zmiana rozmiaru, modyfikacja, rozmiar, dostosowanie
-home.pipeline.title=Automatyzacja (Zaawansowane)
+home.pipeline.title=Automatyzacja
home.pipeline.desc=Wykonaj wiele akcji na dokumentach PDF, tworząc automatyzację
-pipeline.tags=automate,sequence,scripted,batch-process
+pipeline.tags=automatyzacja, sekwencja, skrypt, przetwarzanie wsadowe
home.add-page-numbers.title=Dodaj numery stron
home.add-page-numbers.desc=Dodaj numery strony w dokumencie PDF w podanej lokalizacji
-add-page-numbers.tags=paginate,label,organize,index
+add-page-numbers.tags=stronicowanie, etykieta, organizowanie, indeks, index
home.auto-rename.title=Automatycznie zmień nazwę PDF
home.auto-rename.desc=Automatycznie zmień nazwę PDF bazując na nagłówku
-auto-rename.tags=auto-detect,header-based,organize,relabel
+auto-rename.tags=automatyczne wykrywanie, oparte na nagłówkach, organizowanie, ponowne etykietowanie
home.adjust-contrast.title=Zmień kolor/nasycenie/jasność
home.adjust-contrast.desc=Zmień kolor/nasycenie/jasność w dokumencie PDF
-adjust-contrast.tags=color-correction,tune,modify,enhance
+adjust-contrast.tags=Korekcja kolorów, dostrajanie, modyfikacja, ulepszanie
home.crop.title=Przytnij PDF
home.crop.desc=Przytnij dokument PDF w celu zmniejszenia rozmiaru
-crop.tags=trim,shrink,edit,shape
+crop.tags=przycinanie, zmniejszanie, edycja, kształtowanie
home.autoSplitPDF.title=Automatycznie podziel strony
home.autoSplitPDF.desc=Automatycznie podziel dokument na strony
-autoSplitPDF.tags=QR-based,separate,scan-segment,organize
+autoSplitPDF.tags=Oparty na QR, rozdzielanie, skanowanie, organizowanie
home.sanitizePdf.title=Dezynfekcja
home.sanitizePdf.desc=Usuń skrypt i inne elementy z dokumentu PDF
-sanitizePdf.tags=clean,secure,safe,remove-threats
+sanitizePdf.tags=czyszczenie, ochrona, bezpieczeństwo, usuwanie zagrożeń
home.URLToPDF.title=Strona WWW do PDFa
home.URLToPDF.desc=Zapisuje podany adres WWW do PDFa
-URLToPDF.tags=web-capture,save-page,web-to-doc,archive
+URLToPDF.tags=przechwytywanie stron internetowych, zapisywanie strony, strona internetowa do dokumentu, archiwizacja
home.HTMLToPDF.title=HTML do PDF
home.HTMLToPDF.desc=Zapisuje podany plik HTML/ZIP do PDF
-HTMLToPDF.tags=markup,web-content,transformation,convert
+HTMLToPDF.tags=znaczniki, treść internetowa, transformacja, konwertowanie
home.MarkdownToPDF.title=Markdown do PDF
home.MarkdownToPDF.desc=Zapisuje dokument Markdown do PDF
-MarkdownToPDF.tags=markup,web-content,transformation,convert
+MarkdownToPDF.tags=znaczniki, treść internetowa, transformacja, konwertowanie
home.getPdfInfo.title=Pobierz informacje o pliku PDF
home.getPdfInfo.desc=Pobiera wszelkie informacje o pliku PDF
-getPdfInfo.tags=infomation,data,stats,statistics
+getPdfInfo.tags=informacje, dane, statystyka, statystyki
home.extractPage.title=Wyciągnij stronę z PDF
home.extractPage.desc=Wyciąga stronę z dokumentu PDF
-extractPage.tags=extract
+extractPage.tags=wydobycie,separacja,wyciaganie
home.PdfToSinglePage.title=PDF do jednej strony
home.PdfToSinglePage.desc=Łączy wszystkie strony PDFa w jedną wielką stronę PDF
-PdfToSinglePage.tags=single page
+PdfToSinglePage.tags=pojedyncza strona
home.showJS.title=Pokaż kod JavaScript
@@ -445,61 +465,66 @@ showJS.tags=JS
home.autoRedact.title=Zaciemnij
home.autoRedact.desc=Zaciemnia dokument PDF bazując na podanej wartości
-autoRedact.tags=Redact,Hide,black out,black,marker,hidden
+autoRedact.tags=Redagowanie, ukrywanie, zaciemnianie, zaczernianie, zaznaczanie, ukrywanie
home.tableExtraxt.title=PDF do CSV
home.tableExtraxt.desc=Konwertuje tabele z PDF do pliku CSV
-tableExtraxt.tags=CSV,Table Extraction,extract,convert
+tableExtraxt.tags=CSV, ekstrakcja tabeli, ekstrakcja, konwersja, wydobywanie
home.autoSizeSplitPDF.title=Podziel (Rozmiar/Ilość stron)
home.autoSizeSplitPDF.desc=Rozdziela dokument PDF na wiele dokumentów bazując na podanym rozmiarze, ilości stron bądź ilości dokumentów
-autoSizeSplitPDF.tags=pdf,split,document,organization
+autoSizeSplitPDF.tags=pdf, dzielenie, dokument, organizacja
home.overlay-pdfs.title=Nałóż PDFa
home.overlay-pdfs.desc=Nakłada dokumenty PDF na siebie
-overlay-pdfs.tags=Overlay
+overlay-pdfs.tags=Nakładka
home.split-by-sections.title=Podziel PDF na sekcje
home.split-by-sections.desc=Podziel strony PDF w mniejsze sekcje
-split-by-sections.tags=Section Split, Divide, Customize
+split-by-sections.tags=Podział sekcji, dzielenie, dostosowywanie
home.AddStampRequest.title=Dodaj pieczęć
home.AddStampRequest.desc=Dodaj pieczęć tekstową/obrazową w wyznaczonej lokalizacji dokumentu
-AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
+AddStampRequest.tags=Stempel, dodawanie obrazu, wyśrodkowanie obrazu, znak wodny, PDF, osadzanie, dostosowywanie
home.PDFToBook.title=PDF do eBooka
home.PDFToBook.desc=Zapisuje dokument PDF w formacie eBooka za pomocą Calibre
-PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
+PDFToBook.tags=Książka,komiks,Calibre, konwertowanie, manga, amazon, kindle
home.BookToPDF.title=eBook do PDF
home.BookToPDF.desc=Zapisuje ebooka do PDF za pomocą Calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
-home.removeImagePdf.title=Remove image
-home.removeImagePdf.desc=Remove image from PDF to reduce file size
-removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+home.removeImagePdf.title=Usuń obraz
+home.removeImagePdf.desc=Usuń obraz z pliku PDF, aby zmniejszyć rozmiar pliku
+removeImagePdf.tags=Usuń obraz, operacje na stronie, back-end, strona serwera
+
+
+home.splitPdfByChapters.title=Podziel PDF według rozdziałów
+home.splitPdfByChapters.desc=Podział pliku PDF na wiele plików na podstawie struktury rozdziałów.
+splitPdfByChapters.tags=podział, rozdziały, zakładki, porządkowanie, organizacja
#replace-invert-color
-replace-color.title=Replace-Invert-Color
-replace-color.header=Replace-Invert Color PDF
-home.replaceColorPdf.title=Replace and Invert Color
-home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
-replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
-replace-color.selectText.1=Replace or Invert color Options
-replace-color.selectText.2=Default(Default high contrast colors)
-replace-color.selectText.3=Custom(Customized colors)
-replace-color.selectText.4=Full-Invert(Invert all colors)
-replace-color.selectText.5=High contrast color options
-replace-color.selectText.6=white text on black background
-replace-color.selectText.7=Black text on white background
-replace-color.selectText.8=Yellow text on black background
-replace-color.selectText.9=Green text on black background
-replace-color.selectText.10=Choose text Color
-replace-color.selectText.11=Choose background Color
-replace-color.submit=Replace
+replace-color.title=Zamień-Odwróć-Kolor
+replace-color.header=Zamień-Odwróć kolor PDF
+home.replaceColorPdf.title=Zastąp i Odwróć Kolor
+home.replaceColorPdf.desc=Zastąp kolor tekstu i tła w pliku PDF i odwróć pełen kolor pliku PDF, aby zmniejszyć rozmiar pliku
+replaceColorPdf.tags=Zastąp kolor, operacje na stronach, back-end, strona serwera
+replace-color.selectText.1=Zastąp lub Odwróć opcje kolorów
+replace-color.selectText.2=Domyślnie (domyślne kolory o wysokim kontraście)
+replace-color.selectText.3=Niestandardowe (kolory niestandardowe)
+replace-color.selectText.4=Całkowita-Odwrotność (Odwrócenie wszystkich kolorów)
+replace-color.selectText.5=Wysoki kontrast opcji kolorystycznych
+replace-color.selectText.6=biały tekst na czarnym tle
+replace-color.selectText.7=Czarny tekst na białym tle
+replace-color.selectText.8=Żółty tekst na czarnym tle
+replace-color.selectText.9=Zielony tekst na czarnym tle
+replace-color.selectText.10=Wybierz Kolor tekstu
+replace-color.selectText.11=Wybierz Kolor tła
+replace-color.submit=Zamień
@@ -518,15 +543,17 @@ login.locked=Konto jest zablokowane
login.signinTitle=Zaloguj się
login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego
login.oauth2AutoCreateDisabled=Wyłączono automatyczne tworzenie użytkownika OAUTH2
-login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
+login.oauth2AdminBlockedUser=Rejestracja lub logowanie niezarejestrowanych użytkowników jest obecnie zablokowane. Prosimy o kontakt z administratorem.
login.oauth2RequestNotFound=Błąd logowania OAuth2
login.oauth2InvalidUserInfoResponse=Niewłaściwe dane logowania
login.oauth2invalidRequest=Nieprawidłowe żądanie
login.oauth2AccessDenied=Brak dostępu
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
-login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.userIsDisabled=Użytkownik jest nieaktywny, logowanie przy użyciu tej nazwy użytkownika jest obecnie zablokowane. Prosimy o kontakt z administratorem.
+login.alreadyLoggedIn=Jesteś już zalogowany na
+login.alreadyLoggedIn2=urządzeniach. Wyloguj się z tych urządzeń i spróbuj ponownie.
+login.toManySessions=Masz zbyt wiele aktywnych sesji
#auto-redact
autoRedact.title=Automatyczne zaciemnienie
@@ -750,8 +777,8 @@ removeAnnotations.submit=Usuń
#compare
compare.title=Porównaj
compare.header=Porównaj PDF(y)
-compare.highlightColor.1=Highlight Color 1:
-compare.highlightColor.2=Highlight Color 2:
+compare.highlightColor.1=Kolor Podświetlenia 1:
+compare.highlightColor.2=Kolor Podświetlenia 2:
compare.document.1=Dokument 1
compare.document.2=Dokument 2
compare.submit=Porównaj
@@ -803,7 +830,7 @@ ScannerImageSplit.selectText.7=Minimalny obszar konturu:
ScannerImageSplit.selectText.8=Ustawia próg minimalnego obszaru konturu dla zdjęcia
ScannerImageSplit.selectText.9=Rozmiar obramowania:
ScannerImageSplit.selectText.10=Ustawia rozmiar dodawanego i usuwanego obramowania, aby uniknąć białych obramowań na wyjściu (domyślnie: 1).
-ScannerImageSplit.info=Python is not installed. It is required to run.
+ScannerImageSplit.info=Python nie został zainstalowany. Jest on wymagany do uruchomienia.
#OCR
@@ -830,7 +857,7 @@ ocr.submit=Przetwarzaj PDF za pomocą OCR
extractImages.title=Wyodrębnij obrazy
extractImages.header=Wyodrębnij obrazy
extractImages.selectText=Wybierz format obrazu, na który chcesz przekonwertować wyodrębniony obraz.
-extractImages.allowDuplicates=Save duplicate images
+extractImages.allowDuplicates=Zapisz zduplikowane obrazy
extractImages.submit=Wyodrębnij
@@ -891,8 +918,8 @@ pdfOrganiser.placeholder=(przykład 1,3,2 lub 4-8,2,10-12 lub 2n-1)
#multiTool
-multiTool.title=Multi narzędzie PDF
-multiTool.header=Multi narzędzie PDF
+multiTool.title=Narzędzie Wielofunkcyjne PDF
+multiTool.header=Narzędzie Wielofunkcyjne PDF
multiTool.uploadPrompts=Nazwa pliku
#view pdf
@@ -955,7 +982,7 @@ pdfToImage.color=Kolor
pdfToImage.grey=Odcień szarości
pdfToImage.blackwhite=Czarno-biały (może spowodować utratę danych!)
pdfToImage.submit=Konwertuj
-pdfToImage.info=Python is not installed. Required for WebP conversion.
+pdfToImage.info=Python nie został zainstalowany. Jest wymagany do konwersji WebP.
#addPassword
@@ -992,7 +1019,7 @@ watermark.selectText.6=Odstęp w pionie (odstęp między każdym znakiem wodnym
watermark.selectText.7=Nieprzezroczystość (0% - 100%):
watermark.selectText.8=Typ znaku wodnego:
watermark.selectText.9=Obraz znaku wodnego:
-watermark.selectText.10=Convert PDF to PDF-Image
+watermark.selectText.10=Konwertuj PDF do PDF-Image
watermark.submit=Dodaj znak wodny
watermark.type.1=Tekst
watermark.type.2=Obraz
@@ -1092,7 +1119,7 @@ PDFToXML.submit=Konwertuj
#PDFToCSV
PDFToCSV.title=PDF na CSV
PDFToCSV.header=PDF na CSV
-PDFToCSV.prompt=Choose page to extract table
+PDFToCSV.prompt=Wybierz stronę do wyodrębnienia tabeli
PDFToCSV.submit=Zatwierdź
#split-by-size-or-count
@@ -1154,6 +1181,8 @@ licenses.license=Licencja
survey.nav=Ankieta
survey.title=Ankieta Stirling-PDF
survey.description=Stirling-PDF nie śledzi swoich użytkowników, więc chciałby poznać opinie swoich użytkowników!
+survey.changes=Stirling-PDF zmieniło się od czasu ostatniej ankiety! Aby dowiedzieć się więcej, sprawdź nasz wpis na blogu tutaj:
+survey.changes2=Dzięki tym zmianom otrzymujemy płatne wsparcie biznesowe i finansowanie
survey.please=Wypełnij proszę ankietę dla nas!
survey.disabled=(Blokada wyskakującego okienka z ankieta zostanie dodane w następnych aktualizacjach, ale będzie dostępna na dole strony)
survey.button=Wypełnij ankietę
@@ -1175,7 +1204,21 @@ error.discordSubmit=Discord - wyślij posta z prośbą o pomoc
#remove-image
-removeImage.title=Remove image
-removeImage.header=Remove image
-removeImage.removeImage=Remove image
-removeImage.submit=Remove image
+removeImage.title=Usuń obraz
+removeImage.header=Usuń obraz
+removeImage.removeImage=Usuń obraz
+removeImage.submit=Usuń obraz
+
+
+splitByChapters.title=Podziel PDF według Rozdziałów
+splitByChapters.header=Podziel PDF według Rozdziałów
+splitByChapters.bookmarkLevel=Poziom Zakładek
+splitByChapters.includeMetadata=Dołącz Metadane
+splitByChapters.allowDuplicates=Zezwalaj na Duplikaty
+splitByChapters.desc.1=Narzędzie to dzieli plik PDF na wiele plików PDF w oparciu o strukturę rozdziałów.
+splitByChapters.desc.2=Poziom Zakładek: Wybierz poziom zakładek, który ma zostać użyty do podziału (0 dla najwyższego poziomu, 1 dla drugiego poziomu itd.).
+splitByChapters.desc.3=Dołącz Metadane: Jeśli opcja ta jest zaznaczona, metadane oryginalnego pliku PDF zostaną uwzględnione w każdym rozdzielonych plików PDF.
+splitByChapters.desc.4=Zezwól na Duplikaty: Jeśli ta opcja jest zaznaczona, pozwala na tworzenie oddzielnych plików PDF przez wiele zakładek na tej samej stronie.
+splitByChapters.submit=Podziel PDF
+
+
diff --git a/src/main/resources/messages_pt_BR.properties b/src/main/resources/messages_pt_BR.properties
index b1fc9dee210..acdce05cf9b 100644
--- a/src/main/resources/messages_pt_BR.properties
+++ b/src/main/resources/messages_pt_BR.properties
@@ -3,42 +3,42 @@
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
-addPageNumbers.fontSize=Tamanho da fonte
-addPageNumbers.fontName=Nome da fonte
+addPageNumbers.fontSize=Tamanho da Fonte
+addPageNumbers.fontName=Nome da Fonte
pdfPrompt=Selecione PDF(s)
multiPdfPrompt=Selecione PDFs (2+)
-multiPdfDropPrompt=Selecione (ou arraste e solte) todos os PDFs necessários
-imgPrompt=Selecione a(s) imagem(ns)
+multiPdfDropPrompt=Selecione (ou arraste e solte) todos os PDFs desejados
+imgPrompt=Selecione a(s) Imagem(ns)
genericSubmit=Enviar
processTimeWarning=Aviso: esse processo pode levar até um minuto, dependendo do tamanho do arquivo
-pageOrderPrompt=Ordem de página personalizada (digite uma lista de números de página separada por vírgula):
-pageSelectionPrompt=Seleção de página personalizada (digite uma lista de números de página separada por vírgula 1,5,6 ou Funções como 2n+1) :
+pageOrderPrompt=Ordem de Página Personalizada (Digite uma lista de números de páginas, separadas por vírgula ou Funções como 2n+1):
+pageSelectionPrompt=Seleção de Página Personalizada (Digite uma lista de números de páginas, separadas por vírgula 1,5,6 ou Funções como 2n+1) :
goToPage=Ir
true=Verdadeiro
false=Falso
unknown=Desconhecido
save=Salvar
-saveToBrowser=Salvar no navegador
+saveToBrowser=Salvar no Navegador
close=Fechar
-filesSelected=arquivos selecionados
-noFavourites=Nenhum favorito adicionado
+filesSelected=Arquivos Selecionados
+noFavourites=Nenhum Favorito Adicionado
downloadComplete=Download Completo
-bored=Entediado esperando?
+bored=Entediado Esperando?
alphabet=Alfabeto
downloadPdf=Baixar PDF
text=Texto
font=Fonte
selectFillter=-- Selecione --
-pageNum=Número da página
+pageNum=Número da Página
sizes.small=Pequeno
sizes.medium=Médio
sizes.large=Grande
sizes.x-large=Extra grande
error.pdfPassword=O documento PDF está protegido por senha e a senha não foi fornecida ou está incorreta
-delete=apagar
+delete=Apagar
username=Usuário
password=Senha
-welcome=Bem vindo
+welcome=Bem-vindo
property=Propriedade
black=Preto
white=Branco
@@ -46,7 +46,7 @@ red=Vermelho
green=Verde
blue=Azul
custom=Personalizado...
-WorkInProgess=Em progesso, Talvez não funcione ou apresente erros, Por favor, reporte qualquer problema!
+WorkInProgess=Trabalho em progresso, Talvez não funcione ou apresente erros, Por favor, reporte qualquer problema!
poweredBy=Distribuído por
yes=Sim
no=Não
@@ -55,29 +55,30 @@ notAuthenticatedMessage=Usuário não autenticado.
userNotFoundMessage=Usuário não encontrado.
incorrectPasswordMessage=A senha atual está incorreta.
usernameExistsMessage=Novo Usuário já existe.
-invalidUsernameMessage=Usuário inválido, nome de usuário só pode incluir letras, números e os seguintes caracteres especiais @._+- ou deve ser um email válido.
+invalidUsernameMessage=Usuário inválido, nome de usuário só pode incluir letras, números e os seguintes caracteres especiais @._+- ou deve ser um e-mail válido.
invalidPasswordMessage=A senha não deve estar vazia e não deve conter espaços no início ou no final.
confirmPasswordErrorMessage=Nova Senha e Confirmar Nova Senha devem ser iguais.
-deleteCurrentUserMessage=Não é possível apagar o usuário da sessão atual.
+deleteCurrentUserMessage=Não é possível apagar usuário conectado no momento.
deleteUsernameExistsMessage=O usuário não existe e não pode ser apagado.
-downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário atual
-disabledCurrentUserMessage=O usuário atual não pode ser desativado
+downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário conectado no momento.
+disabledCurrentUserMessage=O usuário atual não pode ser desativado.
downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do usuário atual. Portanto, o usuário atual não será mostrado.
userAlreadyExistsOAuthMessage=O usuário já existe como um usuário OAuth2.
-userAlreadyExistsWebMessage=O usuário já existe como um usuário web.
+userAlreadyExistsWebMessage=O usuário já existe como um usuário Web.
error=Erro
oops=Ops!
help=Ajuda
goHomepage=Ir para a Página Inicial
joinDiscord=Junte-se ao nosso servidor Discord
seeDockerHub=Visite o Docker Hub
-visitGithub=Visite o repositŕio no GitHub
+visitGithub=Visite o repositório no GitHub
donate=Doar
color=Cor
-sponsor=Patrocine
+sponsor=Patrocinador
info=Informações
-page=Page
-pages=Pages
+pro=Pro
+page=Página
+pages=Páginas
legal.privacy=Política de Privacidade
legal.terms=Termos e Condições
@@ -89,7 +90,7 @@ legal.impressum=Informações legais
# Pipeline #
###############
pipeline.header=Menu do Pipeline (Beta)
-pipeline.uploadButton=Upload personalizado
+pipeline.uploadButton=Upload Personalizado
pipeline.configureButton=Configurar
pipeline.defaultOption=Personalizado
pipeline.submitButton=Enviar
@@ -102,16 +103,32 @@ pipeline.deletePrompt=Tem certeza de que deseja excluir o pipeline
######################
pipelineOptions.header=Configuração do Pipeline
pipelineOptions.pipelineNameLabel=Nome do Pipeline
-pipelineOptions.saveSettings=Salvar configurações da operação
+pipelineOptions.saveSettings=Salvar Configurações da Operação
pipelineOptions.pipelineNamePrompt=Insira o nome do pipeline aqui
-pipelineOptions.selectOperation=Selecione uma operação
-pipelineOptions.addOperationButton=Adicione uma operação
+pipelineOptions.selectOperation=Selecione uma Operação
+pipelineOptions.addOperationButton=Adicione uma Operação
pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Baixar
pipelineOptions.validateButton=Validar
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Atualize para versão Pro
+enterpriseEdition.warning=Esse recurso só está disponivel para usuários da versão Pro.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro suporta arquivos de configuração YAML e outros recursos SSO.
+enterpriseEdition.ssoAdvert=Procurando por mais recursos de controle de usuários? Veja Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Você quer melhorar Stirling PDF?
+analytics.paragraph1=Stirling PDF possui coleta de dados opcional para ajudar a melhorar o produto. Nós não rastreamos nenhuma informação pessoal ou conteúdo dos arquivos.
+analytics.paragraph2=Por favor considere habilitar coleta de dados para ajudar Stirling-PDF a crescer e nos ajudar a entender nossos usuários melhor.
+analytics.enable=Habilitar coleta de dados
+analytics.disable=Desabilitar coleta de dados
+analytics.settings=Você pode alterar as configurações de coleta de dados no arquivo config/settings.yml
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Converter de PDF
navbar.sections.security=Assinatura & Segurança
navbar.sections.advance=Avançado
navbar.sections.edit=Visualizar & editar
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -144,61 +162,61 @@ settings.zipThreshold=Compactar arquivos quando o número de arquivos baixados e
settings.signOut=Sair
settings.accountSettings=Configurações de conta
settings.bored.help=Habilitar jogos secretos
-settings.cacheInputs.name=Salvar entradas de formulário
+settings.cacheInputs.name=Salvar entradas do formulário
settings.cacheInputs.help=Habilitar para armazenar entradas usadas anteriormente para execuções futuras
-changeCreds.title=Alterar credenciais
-changeCreds.header=Atualizar detalhes da sua conta
+changeCreds.title=Alterar Credenciais
+changeCreds.header=Atualizar Detalhes da Conta
changeCreds.changePassword=Você está usando as credenciais padrões. Por favor, insira uma nova senha
-changeCreds.newUsername=Novo usuário
-changeCreds.oldPassword=Senha atual
-changeCreds.newPassword=Nova senha
-changeCreds.confirmNewPassword=Confirmar nova senha
-changeCreds.submit=Enviar alterações
-
-
-
-account.title=Configurações de conta
-account.accountSettings=Configurações de conta
-account.adminSettings=Configurações de Administrador – visualizar e adicionar usuários
-account.userControlSettings=Configurações de controle de usuário
-account.changeUsername=Alterar usuário
-account.newUsername=Novo usuário
-account.password=Senha de confirmação
-account.oldPassword=Senha antiga
-account.newPassword=Senha nova
-account.changePassword=Alterar a senha
-account.confirmNewPassword=Confirme a nova senha
+changeCreds.newUsername=Novo Usuário
+changeCreds.oldPassword=Senha Atual
+changeCreds.newPassword=Senha Nova
+changeCreds.confirmNewPassword=Confirme a Nova Senha
+changeCreds.submit=Enviar Alterações
+
+
+
+account.title=Configurações de Conta
+account.accountSettings=Configurações de Conta
+account.adminSettings=Configurações de Administrador – Visualizar e Adicionar Usuários
+account.userControlSettings=Configurações de Controle de Usuário
+account.changeUsername=Alterar Usuário
+account.newUsername=Novo Usuário
+account.password=Senha de Confirmação
+account.oldPassword=Senha Antiga
+account.newPassword=Senha Nova
+account.changePassword=Alterar a Senha
+account.confirmNewPassword=Confirme a Nova Senha
account.signOut=Sair
account.yourApiKey=Sua chave de API
account.syncTitle=Sincronize as configurações do navegador com a conta
-account.settingsCompare=Comparação de configurações:
+account.settingsCompare=Comparação de Configurações:
account.property=Propriedade
account.webBrowserSettings=Configuração do navegador Web
-account.syncToBrowser=Sincronizar conta -> Navegador
-account.syncToAccount=Sincronizar conta <- navegador
+account.syncToBrowser=Sincronizar Conta -> Navegador
+account.syncToAccount=Sincronizar Conta <- Navegador
adminUserSettings.title=Configurações de controle de usuário
-adminUserSettings.header=Configurações de controle de usuário administrador
+adminUserSettings.header=Configurações de controle do usuário administrador
adminUserSettings.admin=Administrador
adminUserSettings.user=Usuário
adminUserSettings.addUser=Adicionar novo usuário
adminUserSettings.deleteUser=Apagar usuário
adminUserSettings.confirmDeleteUser=O usuário deve ser apagado?
adminUserSettings.confirmChangeUserStatus=O usuário deve ser desabilitado/habilitado?
-adminUserSettings.usernameInfo=Nome de usuário só pode incluir letras, números e os seguintes caracteres especiais @._+- ou deve ser um email válido.
+adminUserSettings.usernameInfo=Nome de usuário só pode incluir letras, números e os seguintes caracteres especiais @._+- ou deve ser um e-mail válido.
adminUserSettings.roles=Funções
adminUserSettings.role=Função
adminUserSettings.actions=Ações
adminUserSettings.apiUser=Usuário de API limitado
adminUserSettings.extraApiUser=Usuário de API limitado adicional
-adminUserSettings.webOnlyUser=Usuário apenas web
+adminUserSettings.webOnlyUser=Usuário web apenas
adminUserSettings.demoUser=Usuário demo (Sem configurações personalizadas)
adminUserSettings.internalApiUser=Usuário interno de API
adminUserSettings.forceChange=Forçar usuário a trocar a senha ao iniciar sessão
-adminUserSettings.submit=Salvar usuário
-adminUserSettings.changeUserRole=Alterar Função de Usuário
+adminUserSettings.submit=Salvar Usuário
+adminUserSettings.changeUserRole=Alterar Função do Usuário
adminUserSettings.authenticated=Autenticado
adminUserSettings.editOwnProfil=Editar próprio perfil
adminUserSettings.enabledUser=usuário habilitado
@@ -217,14 +235,16 @@ database.fileSize=Tamanho do arquivo
database.deleteBackupFile=Apagar arquivo de backup
database.importBackupFile=Importar arquivo de backup
database.downloadBackupFile=Baixar arquivo de backup
-database.info_1=Ao importar dados, é crucial garantir a estrutura correta. Se você não tem certeza do que está fazendo procure auxílio de um profissional. Um erro na estrutura pode ocasionar em mau funcionamento da aplicação, incluindo a possibilidade de perda total da aplicação.
+database.info_1=Ao importar dados, é crucial garantir a estrutura correta. Se você não tem certeza do que está fazendo procure auxílio de um profissional. Um erro na estrutura pode ocasionar em mau funcionamento da aplicação, incluindo a impossibilidade da aplicação ser executada.
database.info_2=O nome do arquivo não importa ao enviar. Ele será renomeado em seguida para seguir o formato backup_user_yyyyMMddHHmm.sql, garantindo uma convenção de nomes coerente.
database.submit=Importar Backup
database.importIntoDatabaseSuccessed=Importação para o banco de dados bem sucedida
database.fileNotFound=Arquivo não encontrado
-database.fileNullOrEmpty=O arquivo não estar nulo ou vazio
+database.fileNullOrEmpty=O arquivo não pode estar nulo ou vazio
database.failedImportFile=Falha ao importar arquivo
+session.expired=Sua sessão expirou. Por gentileza atualize a página e tente novamente.
+
#############
# HOME-PAGE #
#############
@@ -237,11 +257,11 @@ home.viewPdf.desc=Visualizar, anotar, adicionar texto ou imagens
viewPdf.tags=visualizar,ler,anotar,texto,imagem
home.multiTool.title=Multiferramenta de PDF
-home.multiTool.desc=Mesclar, girar, reorganizar e remover páginas
-multiTool.tags=Multiferramenta,Múltiplas operações,Interface do Usuário,Clique e arraste,front-end,lado do cliente,interativo,intratável,movimento
+home.multiTool.desc=Mesclar, girar, reorganizar, dividir e remover páginas
+multiTool.tags=Multiferramenta, múltiplas operações, Interface do Usuário, Clique e arraste, front-end, lado do cliente, interativo, intratável, movimento, excluir, migrar, dividir
home.merge.title=Mesclar
-home.merge.desc=Mesclar facilmente vários PDFs em um só.
+home.merge.desc=Mescle facilmente vários PDFs em um só.
merge.tags=mesclar,Operações de Página,Back-end,lado do servidor
home.split.title=Dividir
@@ -249,7 +269,7 @@ home.split.desc=Dividir PDFs em vários documentos
split.tags=Operações de Página,dividir,Múltiplas Páginas,cortar,lado do servidor
home.rotate.title=Girar
-home.rotate.desc=Girar facilmente seus PDFs.
+home.rotate.desc=Gire facilmente seus PDFs.
rotate.tags=Lado do servidor
@@ -267,11 +287,11 @@ pdfOrganiser.tags=duplex,par,ímpar,ordenar,mover
home.addImage.title=Adicionar Imagem
-home.addImage.desc=Adicionar uma imagem em um local definido no PDF (Em desenvolvimento)
+home.addImage.desc=Adicionar uma imagem em um local definido no PDF
addImage.tags=img,jpg,imagem,foto
-home.watermark.title="Adicionar Marca d'água"
-home.watermark.desc="Adicionar uma marca d'água personalizada ao seu documento PDF."
+home.watermark.title=Adicionar Marca d'água
+home.watermark.desc=Adicionar uma marca d'água personalizada ao seu documento PDF.
watermark.tags=Texto,repetindo,rótulo,próprio,direitos autorais,marca registrada,img,jpg,imagem,foto
home.permissions.title=Alterar Permissões
@@ -283,13 +303,13 @@ home.removePages.title=Remover
home.removePages.desc=Excluir as páginas indesejadas do seu documento PDF.
removePages.tags=Remover páginas,excluir páginas
-home.addPassword.title=Adicionar senha
+home.addPassword.title=Adicionar Senha
home.addPassword.desc=Criptografar seu documento PDF com uma senha.
addPassword.tags=seguro,segurança
home.removePassword.title=Remover Senha
home.removePassword.desc=Remover a proteção por senha do seu documento PDF.
-removePassword.tags=seguro, Descriptografar, segurança, remover senha
+removePassword.tags=seguro, descriptografar, segurança, remover senha
home.compressPdfs.title=Comprimir
home.compressPdfs.desc=Comprimir PDFs para reduzir o tamanho do arquivo.
@@ -319,7 +339,7 @@ pdfToPDFA.tags=arquivo,longo prazo,padrão,conversão,armazenamento,preservaçã
home.PDFToWord.title=PDF para Word
home.PDFToWord.desc=Converter PDF para formatos Word (DOC, DOCX e ODT)
-PDFToWord.tags=doc,docx,odt,word,transformação,formato,conversão,escritório,microsoft,arquivo doc
+PDFToWord.tags=doc,docx,odt,word,transformação,formato,conversão,escritório,microsoft,doc
home.PDFToPresentation.title=PDF para Apresentação
home.PDFToPresentation.desc=Converter PDF para formatos de apresentação (PPT, PPTX e ODP)
@@ -335,12 +355,12 @@ PDFToHTML.tags=conteúdo web,compatível com navegador
home.PDFToXML.title=PDF para XML
-home.PDFToXML.desc=Converter PDF para o formato XML
+home.PDFToXML.desc=Converter PDF para formato XML
PDFToXML.tags=extração-de-dados,conteúdo-estruturado,interoperabilidade,transformação,converter
home.ScannerImageSplit.title=Detectar/Dividir Fotos Digitalizadas
-home.ScannerImageSplit.desc=Divide várias fotos de dentro de uma imagem/PDF digitalizado
-ScannerImageSplit.tags=separar,detecção-automática,digitalizações,foto-múltipla,organizar
+home.ScannerImageSplit.desc=Divide várias fotos de dentro de uma imagem/PDF
+ScannerImageSplit.tags=separar,detecção-automática,digitalizações,fotos-múltiplas,organizar
home.sign.title=Assinar
home.sign.desc=Adicionar assinatura ao PDF por desenho, texto ou imagem
@@ -358,7 +378,7 @@ home.removeBlanks.title=Remover Páginas em Branco
home.removeBlanks.desc=Detectar e remover páginas em branco de um documento
removeBlanks.tags=limpeza,otimização,sem-conteúdo,organizar
-home.removeAnnotations.title=Remover anotações
+home.removeAnnotations.title=Remover Anotações
home.removeAnnotations.desc=Remove todos os comentários/anotações de um PDF
removeAnnotations.tags=comentários,destaque,notas,marcação,remover
@@ -382,7 +402,7 @@ home.scalePages.title=Ajustar Tamanho/Escala de Página
home.scalePages.desc=Alterar o tamanho/escala da página e/ou seu conteúdo.
scalePages.tags=redimensionar,modificar,dimensão,adaptar
-home.pipeline.title=Pipeline (Avançado)
+home.pipeline.title=Pipeline
home.pipeline.desc=Executar várias ações em PDFs definindo scripts de pipeline
pipeline.tags=automatizar,sequência,scriptado,processo-em-lote
@@ -406,7 +426,7 @@ home.autoSplitPDF.title=Divisão Automática de Páginas
home.autoSplitPDF.desc=Dividir automaticamente um PDF digitalizado com separador de páginas físicas QR Code
autoSplitPDF.tags=baseado-em-QR,separar,segmento-de-digitalização,organizar
-home.sanitizePdf.title=Sanitizar
+home.sanitizePdf.title=Higienizar
home.sanitizePdf.desc=Remover scripts e outros elementos de arquivos PDF
sanitizePdf.tags=limpar,seguro,protegido,remover-ameaças
@@ -448,7 +468,7 @@ home.autoRedact.desc=Ocultação automática (escurecimento) de texto em um PDF
autoRedact.tags=Redigir,ocultar,escurecer,preto,marcador,oculto
home.tableExtraxt.title=PDF para CSV
-home.tableExtraxt.desc=Extrai tabelas de um PDF convertendo-o para CSV
+home.tableExtraxt.desc=Extrai tabelas de um PDF convertendo para CSV
tableExtraxt.tags=CSV,extração de tabela,extrair,converter
@@ -461,13 +481,13 @@ home.overlay-pdfs.title=Sobrepor PDFs
home.overlay-pdfs.desc=Sobrepõe PDFs sobre outro PDF
overlay-pdfs.tags=Sobreposição
-home.split-by-sections.title=Dividir PDF por seções
+home.split-by-sections.title=Dividir PDF por Seções
home.split-by-sections.desc=Divida cada página de um PDF em seções horizontais e verticais menores
split-by-sections.tags=Seção Dividir, Dividir, Personalizar
-home.AddStampRequest.title=Adicionar carimbo ao PDF
+home.AddStampRequest.title=Adicionar Carimbo ao PDF
home.AddStampRequest.desc=Adicione texto ou carimbos de imagem em locais definidos
-AddStampRequest.tags="Carimbo,Adicionar imagem,centralizar imagem,Marca d'água,PDF,Incorporar,Personalizar"
+AddStampRequest.tags=Carimbo,Adicionar imagem,centralizar imagem,Marca d'água,PDF,Incorporar,Personalizar
home.PDFToBook.title=PDF para Livro
@@ -475,31 +495,36 @@ home.PDFToBook.desc=Converte PDF para formatos de livro/quadrinhos usando Calibr
PDFToBook.tags=Livro,Quadrinhos,Calibre,Converter,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
home.BookToPDF.title=Livro para PDF
-home.BookToPDF.desc=Converte formatos de livros/quadrinhos em PDF usando Calibre
+home.BookToPDF.desc=Converte formatos de livros/quadrinhos para PDF usando Calibre
BookToPDF.tags=Livro,Quadrinhos,Calibre,Converter,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
-home.removeImagePdf.title=Remover imagem
+home.removeImagePdf.title=Remover Imagem
home.removeImagePdf.desc=Remova a imagem do PDF para reduzir o tamanho do arquivo
removeImagePdf.tags=Remover imagem,operações de página,back-end,lado do servidor
+
+home.splitPdfByChapters.title=Divide PDF por Capítulos
+home.splitPdfByChapters.desc=Divide um PDF em vários arquivos baseado na sua estrutura de capítulos.
+splitPdfByChapters.tags=dividir,capítulos,favoritos,organizar
+
#replace-invert-color
-replace-color.title=Replace-Invert-Color
-replace-color.header=Replace-Invert Color PDF
-home.replaceColorPdf.title=Replace and Invert Color
-home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
-replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
-replace-color.selectText.1=Replace or Invert color Options
-replace-color.selectText.2=Default(Default high contrast colors)
-replace-color.selectText.3=Custom(Customized colors)
-replace-color.selectText.4=Full-Invert(Invert all colors)
-replace-color.selectText.5=High contrast color options
-replace-color.selectText.6=white text on black background
-replace-color.selectText.7=Black text on white background
-replace-color.selectText.8=Yellow text on black background
-replace-color.selectText.9=Green text on black background
-replace-color.selectText.10=Choose text Color
-replace-color.selectText.11=Choose background Color
-replace-color.submit=Replace
+replace-color.title=Substituir-Inverter-Cor
+replace-color.header=Substitui-Inverter Cor PDF
+home.replaceColorPdf.title=Substitui e Inverte Cor
+home.replaceColorPdf.desc=Substitui cor de um texto e plano de fundo de um PDF e inverte a toda cor do PDF para reduzir o tamanho
+replaceColorPdf.tags=Substitui Cor, Operações na Página, back end, lado do servidor
+replace-color.selectText.1=Substituir ou inverter cor Opções
+replace-color.selectText.2=Padrão(Padrão cores de alto constraste)
+replace-color.selectText.3=Customizado(Cores customizadas)
+replace-color.selectText.4=Inversão Completa(Inverte todas cores)
+replace-color.selectText.5=Opções de cores de alto contraste
+replace-color.selectText.6=Texto branco em um plano de fundo preto
+replace-color.selectText.7=Texto preto em um plano de fundo branco
+replace-color.selectText.8=Texto amarelo em um plano de fundo preto
+replace-color.selectText.9=Texto verde em um plano de fundo preto
+replace-color.selectText.10=Escolha cor do texto
+replace-color.selectText.11=Escolha cor do plano de fundo
+replace-color.submit=Substituir
@@ -516,21 +541,23 @@ login.rememberme=Lembrar de mim
login.invalid=Usuário ou senha inválidos.
login.locked=Sua conta foi bloqueada.
login.signinTitle=Por favor, inicie a sessão
-login.ssoSignIn=Iniciar sessão através de início de sessão único
+login.ssoSignIn=Iniciar sessão através de login único (SSO)
login.oauth2AutoCreateDisabled=Auto-Criar Usuário OAUTH2 Desativado
login.oauth2AdminBlockedUser=O registro ou login de usuários não registrados está atualmente bloqueado. Entre em contato com o administrador.
login.oauth2RequestNotFound=Solicitação de autorização não encontrada
login.oauth2InvalidUserInfoResponse=Resposta de informação de usuário inválida
-login.oauth2invalidRequest=Requisição inválida
-login.oauth2AccessDenied=Acesso negado
-login.oauth2InvalidTokenResponse=Resposta de token inválida
-login.oauth2InvalidIdToken=Id de token inválido
+login.oauth2invalidRequest=Requisição Inválida
+login.oauth2AccessDenied=Acesso Negado
+login.oauth2InvalidTokenResponse=Resposta de Token Inválida
+login.oauth2InvalidIdToken=Id de Token Inválido
login.userIsDisabled=O usuário está desativado, o login está atualmente bloqueado com este nome de usuário. Entre em contato com o administrador.
-
+login.alreadyLoggedIn=Você já está conectado
+login.alreadyLoggedIn2=aparelhos. Por favor saia dos aparelhos e tente novamente.
+login.toManySessions=Você tem muitas sessões ativas
#auto-redact
-autoRedact.title=Redigir automaticamente
-autoRedact.header=Redigir automaticamente
+autoRedact.title=Redação Automática de Dados
+autoRedact.header=Redação Automática de Dados
autoRedact.colorLabel=Cor
autoRedact.textsToRedactLabel=Texto para redigir (separado por linha)
autoRedact.textsToRedactPlaceholder=por exemplo: \nConfidencial \nSecreto
@@ -616,22 +643,22 @@ AddStampRequest.fontSize=Tamanho da fonte/imagem
AddStampRequest.rotation=Rotação
AddStampRequest.opacity=Opacidade
AddStampRequest.position=Posição
-AddStampRequest.overrideX=Substituir coordenada X
-AddStampRequest.overrideY=Substituir coordenada Y
+AddStampRequest.overrideX=Substituir Coordenada X
+AddStampRequest.overrideY=Substituir Coordenada Y
AddStampRequest.customMargin=Margem personalizada
AddStampRequest.customColor=Cor de texto personalizada
AddStampRequest.submit=Enviar
#sanitizePDF
-sanitizePDF.title=Sanitizar PDF
-sanitizePDF.header=Sanitizar um arquivo PDF
+sanitizePDF.title=Higienizar PDF
+sanitizePDF.header=Higienizar um arquivo PDF
sanitizePDF.selectText.1=Remover ações de JavaScript
sanitizePDF.selectText.2=Remover arquivos embutidos
sanitizePDF.selectText.3=Remover metadados
sanitizePDF.selectText.4=Remover links
sanitizePDF.selectText.5=Remover fontes
-sanitizePDF.submit=Sanitizar PDF
+sanitizePDF.submit=Higienizar PDF
#addPageNumbers
@@ -711,9 +738,9 @@ certSign.title=Assinatura com Certificado
certSign.header=Assine um PDF com o seu certificado (Em desenvolvimento)
certSign.selectPDF=Selecione um arquivo PDF para assinatura:
certSign.jksNote=Nota: Se o seu tipo de certificado não estiver listado abaixo, converta-o em um arquivo Java Keystore (.jks) usando a ferramenta de linha de comando keytool. Em seguida, escolha a opção de arquivo .jks abaixo.
-certSign.selectKey="Selecione o seu arquivo de chave privada (formato PKCS#8, pode ser .pem ou .der):"
+certSign.selectKey=Selecione o seu arquivo de chave privada (formato PKCS#8, pode ser .pem ou .der):
certSign.selectCert=Selecione o seu arquivo de certificado (formato X.509, pode ser .pem ou .der):
-certSign.selectP12="Selecione o seu arquivo de armazenamento de chave PKCS#12 (.p12 ou .pfx) (opcional, se fornecido, deve conter a sua chave privada e certificado):"
+certSign.selectP12=Selecione o seu arquivo de armazenamento de chave PKCS#12 (.p12 ou .pfx) (opcional, se fornecido, deve conter a sua chave privada e certificado):
certSign.selectJKS=Selecione seu arquivo Java Keystore (.jks ou .keystore):
certSign.certType=Tipo de Certificado
certSign.password=Digite a senha do seu armazenamento de chave ou chave privada (se aplicável):
@@ -725,7 +752,7 @@ certSign.submit=Assinar PDF
#removeCertSign
-removeCertSign.title=Remover assinatura com Certificado
+removeCertSign.title=Remover Assinatura do Certificado
removeCertSign.header=Remover o certificado digital do PDF
removeCertSign.selectPDF=Selecione um arquivo PDF:
removeCertSign.submit=Remover assinatura
@@ -737,13 +764,13 @@ removeBlanks.header=Remover páginas em branco
removeBlanks.threshold=Limite de brancura de pixel:
removeBlanks.thresholdDesc=Limite para determinar o quão branco um pixel branco deve ser para ser classificado como 'Branco'. 0 = Preto, 255 branco puro.
removeBlanks.whitePercent=Porcentagem de Branco (%):
-removeBlanks.whitePercentDesc=Porcentagem de páginas que devem ter pixels “brancos” para serem removidas
+removeBlanks.whitePercentDesc=Porcentagem da página que devem ter pixels “brancos” para serem removidas
removeBlanks.submit=Remover páginas em branco
#removeAnnotations
-removeAnnotations.title=Remover anotações
-removeAnnotations.header=Remover anotações
+removeAnnotations.title=Remover Anotações
+removeAnnotations.header=Remover Anotações
removeAnnotations.submit=Remover
@@ -809,26 +836,26 @@ ScannerImageSplit.info=Python não está instalado. É necessário para executar
#OCR
ocr.title=OCR / Limpeza de Digitalização
ocr.header=OCR / Limpeza de Digitalização (Reconhecimento Óptico de Caracteres)
-ocr.selectText.1=Selecione os idiomas a serem detectados no PDF (os listados são os atualmente detectados):
-ocr.selectText.2=Criar um arquivo de texto contendo o texto OCR ao lado do PDF com OCR
+ocr.selectText.1=Selecione os idiomas a serem detectados no PDF (os listados são os atualmente instalados):
+ocr.selectText.2=Criar um arquivo de texto contendo o texto OCR junto do PDF com OCR
ocr.selectText.3=Páginas corretamente digitalizadas em um ângulo inclinado, gire-as de volta à posição original
ocr.selectText.4=Limpar a página para reduzir a probabilidade de o OCR encontrar texto no ruído de fundo (sem alteração na saída)
ocr.selectText.5=Limpar a página para reduzir a probabilidade de o OCR encontrar texto no ruído de fundo, mantendo a limpeza na saída.
-ocr.selectText.6=Ignorar páginas com texto interativo, processar apenas as páginas de OCR que são imagens
+ocr.selectText.6=Ignorar páginas com texto interativo, processar por OCR apenas as páginas com imagens
ocr.selectText.7=Forçar OCR, executar OCR em todas as páginas, removendo todos os elementos de texto originais
ocr.selectText.8=Normal (gerará um erro se o PDF já contiver texto)
-ocr.selectText.9=Configurações adicionais
+ocr.selectText.9=Configurações Adicionais
ocr.selectText.10=Modo OCR
ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão)
-ocr.selectText.12=Tipo de renderização (avançado)
+ocr.selectText.12=Tipo de Renderização (avançado)
ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker
ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR.
ocr.submit=Processar PDF com OCR
#extractImages
-extractImages.title=Extrair imagens
-extractImages.header=Extrair imagens
+extractImages.title=Extrair Imagens
+extractImages.header=Extrair Imagens
extractImages.selectText=Selecione o formato de imagem para converter as imagens extraídas
extractImages.allowDuplicates=Salvar imagens duplicadas
extractImages.submit=Extrair
@@ -856,7 +883,7 @@ compress.submit=Comprimir
#Add image
-addImage.title=Adicionar imagem
+addImage.title=Adicionar Imagem
addImage.header=Adicionar imagem ao PDF
addImage.everyPage=Para cada página?
addImage.upload=Enviar imagem
@@ -865,7 +892,7 @@ addImage.submit=Adicionar imagem
#merge
merge.title=Mesclar
-merge.header=Mesclar Vários PDFs (2+)
+merge.header=Mesclar vários PDFs (2+)
merge.sortByName=Classificar por nome
merge.sortByDate=Classificar por data
merge.removeCertSign=Remover a assinatura digital do arquivo mesclado?
@@ -879,7 +906,7 @@ pdfOrganiser.submit=Reorganizar páginas
pdfOrganiser.mode=Modo
pdfOrganiser.mode.1=Ordem de página personalizada
pdfOrganiser.mode.2=Ordem inversa
-pdfOrganiser.mode.3=Classificação Duplex
+pdfOrganiser.mode.3=Classificação duplex
pdfOrganiser.mode.4=Classificação de livreto
pdfOrganiser.mode.5=Classificação de livreto com ponto lateral
pdfOrganiser.mode.6=Divisão ímpar-par
@@ -944,7 +971,7 @@ imageToPDF.selectText.5=Converter em PDFs separados
#pdfToImage
-pdfToImage.title=PDF para imagem
+pdfToImage.title=PDF para Imagem
pdfToImage.header=Converter PDF para imagem
pdfToImage.selectText=Formato de imagem
pdfToImage.singleOrMultiple=Tipo de resultado de imagem
@@ -953,7 +980,7 @@ pdfToImage.multi=Várias imagens, uma imagem por página
pdfToImage.colorType=Tipo de cor
pdfToImage.color=Colorida
pdfToImage.grey=Escala de Cinza
-pdfToImage.blackwhite=Preto e Branco (pode perder de dados!)
+pdfToImage.blackwhite=Preto e Branco (pode perder informações!)
pdfToImage.submit=Converter
pdfToImage.info=Python não está instalado. Necessário para conversão WebP.
@@ -964,7 +991,7 @@ addPassword.header=Adicionar Senha (Criptografar)
addPassword.selectText.1=Selecione o PDF para Criptografar
addPassword.selectText.2=Senha
addPassword.selectText.3=Tamanho da Chave de Criptografia
-addPassword.selectText.4=Valores mais altos são mais seguros, mas valores mais baixos são mais compatíveis.
+addPassword.selectText.4=Valores mais altos são mais seguros, mas valores mais baixos são melhores para compatibilidade.
addPassword.selectText.5=Permissões a serem definidas (recomendado para uso junto com a senha do proprietário)
addPassword.selectText.6=Impedir a montagem do documento
addPassword.selectText.7=Impedir a extração de conteúdo
@@ -981,26 +1008,26 @@ addPassword.submit=Criptografar
#watermark
-watermark.title="Adicionar marca d'água"
-watermark.header="Adicionar marca d'água"
-watermark.selectText.1="Selecione PDF para adicionar a marca d'água:"
-watermark.selectText.2="Texto da marca d'água:"
+watermark.title=Adicionar marca d'água
+watermark.header=Adicionar marca d'água
+watermark.selectText.1=Selecione PDF para adicionar a marca d'água:
+watermark.selectText.2=Texto da marca d'água:
watermark.selectText.3=Tamanho da fonte:
watermark.selectText.4=Rotação (0-360):
-watermark.selectText.5="widthSpacer (Espaço entre cada marca d'água horizontalmente):"
-watermark.selectText.6="heightSpacer (Espaço entre cada marca d'água verticalmente):"
+watermark.selectText.5=widthSpacer (Espaço entre cada marca d'água horizontalmente):
+watermark.selectText.6=heightSpacer (Espaço entre cada marca d'água verticalmente):
watermark.selectText.7=Opacidade (0% - 100%):
-watermark.selectText.8="Tipo de marca d'água:"
-watermark.selectText.9="Imagem da marca d'água:"
+watermark.selectText.8=Tipo de marca d'água:
+watermark.selectText.9=Imagem da marca d'água:
watermark.selectText.10=Converter PDF em imagem PDF
-watermark.submit="Adicionar marca d'água"
+watermark.submit=Adicionar marca d'água
watermark.type.1=Texto
watermark.type.2=Imagem
#Change permissions
-permissions.title=Alterar permissões
-permissions.header=Alterar permissões
+permissions.title=Alterar Permissões
+permissions.header=Alterar Permissões
permissions.warning=Aviso: para que essas permissões sejam imutáveis, é recomendável defini-las com uma senha através da página Adicionar Senha
permissions.selectText.1=Selecione o PDF para alterar as permissões
permissions.selectText.2=Permissões para definir
@@ -1016,7 +1043,7 @@ permissions.submit=Alterar
#remove password
-removePassword.title=Remover senha
+removePassword.title=Remover Senha
removePassword.header=Remover senha (descriptografar)
removePassword.selectText.1=Selecione o PDF para descriptografar
removePassword.selectText.2=Senha
@@ -1024,7 +1051,7 @@ removePassword.submit=Remover
#changeMetadata
-changeMetadata.title=Alterar metadados
+changeMetadata.title=Alterar Metadados
changeMetadata.header=Alterar metadados
changeMetadata.selectText.1=Edite as variáveis que deseja alterar
changeMetadata.selectText.2=Excluir todos os metadados
@@ -1108,7 +1135,7 @@ split-by-size-or-count.submit=Enviar
#overlay-pdfs
-overlay-pdfs.header=Sobrepor arquivos PDF
+overlay-pdfs.header=Sobrepor Arquivos PDF
overlay-pdfs.baseFile.label=Selecione o arquivo PDF base
overlay-pdfs.overlayFiles.label=Selecione os arquivos PDF para sobreposição
overlay-pdfs.mode.label=Selecione o modo de sobreposição
@@ -1126,7 +1153,7 @@ overlay-pdfs.submit=Enviar
#split-by-sections
split-by-sections.title=Dividir PDF por seções
split-by-sections.header=Divida o PDF em seções
-split-by-sections.horizontal.label=Divisões horizontais
+split-by-sections.horizontal.label=Divisões Horizontais
split-by-sections.vertical.label=Divisões Verticais
split-by-sections.horizontal.placeholder=Insira o número de divisões horizontais
split-by-sections.vertical.placeholder=Insira o número de divisões verticais
@@ -1144,8 +1171,8 @@ printFile.submit=Imprimir
#licenses
licenses.nav=Licenças
-licenses.title=Licenças de terceiros
-licenses.header=Licenças de terceiros
+licenses.title=Licenças de Terceiros
+licenses.header=Licenças de Terceiros
licenses.module=Módulo
licenses.version=Versão
licenses.license=Licença
@@ -1154,8 +1181,10 @@ licenses.license=Licença
survey.nav=Pesquisa
survey.title=Pesquisa Stirling-PDF
survey.description=Stirling-PDF não tem rastreamento, então queremos ouvir nossos usuários para melhorar o Stirling-PDF!
+survey.changes=Stirling-PDF mudou desde o a última pesquisa! Para saber mais acesse nosso post no blog:
+survey.changes2=Com essas mudanças estamos implementando suporte empresarial pago e financeamento
survey.please=Por favor, considere responder à nossa pesquisa!
-survey.disabled=(O pop-up da pesquisa será desativado nas atualizações seguintes, mas estará disponível no final da página)
+survey.disabled=(O pop-up da pesquisa será desativado nas atualizações seguintes, mas estará disponível no rodapé da página)
survey.button=Responda a pesquisa
survey.dontShowAgain=Não mostre novamente
@@ -1163,11 +1192,11 @@ survey.dontShowAgain=Não mostre novamente
#error
error.sorry=Desculpe pelo problema!
error.needHelp=Precisa de ajuda / Encontrou um problema?
-error.contactTip=Se você ainda estiver com problemas, não hesite em entrar em contato conosco para obter ajuda. Você pode enviar um ticket em nossa página GitHub ou entrar em contato conosco através do Discord:
+error.contactTip=Se você ainda estiver com problemas, não hesite em entrar em contato conosco para obter ajuda. Você pode enviar um tíquete em nossa página GitHub ou entrar em contato conosco através do Discord:
error.404.head=404 - Página não encontrada | Ops, tropeçamos no código!
error.404.1=Não conseguimos encontrar a página que você está procurando.
error.404.2=Algo deu errado
-error.github=Submeter um ticket no GitHub
+error.github=Submeter um tíquete no GitHub
error.showStack=Mostrar rastreamento de pilha
error.copyStack=Copiar rastreamento de pilha
error.githubSubmit=GitHub - Submeter um tíquete
@@ -1175,7 +1204,21 @@ error.discordSubmit=Discord - Submeter um post de suporte
#remove-image
-removeImage.title=Remover imagem
+removeImage.title=Remover Imagem
removeImage.header=Remover imagem
removeImage.removeImage=Remover imagem
removeImage.submit=Remover imagem
+
+
+splitByChapters.title=Dividir PDF por Capítulos
+splitByChapters.header=Dividir PDF por Capítulos
+splitByChapters.bookmarkLevel=Nível de Marcador
+splitByChapters.includeMetadata=Incluir Metadados
+splitByChapters.allowDuplicates=Permitir Cópias
+splitByChapters.desc.1=Essa ferramenta divide um arquivo PDF em vários arquivos PDFs baseado na estrutura de cápitulos.
+splitByChapters.desc.2=Nível de Marcador: Escolha o nível de marcador a ser usado para divisão (0 para o primeiro nível, 1 para o segundo nível, etc).
+splitByChapters.desc.3=Incluir Metadados: Se marcado, os metadados do PDF original serão incluidos em cada divisão do PDF.
+splitByChapters.desc.4=Permitir Cópias: Se marcado, habilita vários marcadores na mesma página para criar PDFs separados.
+splitByChapters.submit=Dividir PDF
+
+
diff --git a/src/main/resources/messages_pt_PT.properties b/src/main/resources/messages_pt_PT.properties
index 7f6e2956828..94b2e53569d 100644
--- a/src/main/resources/messages_pt_PT.properties
+++ b/src/main/resources/messages_pt_PT.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Descarregar
pipelineOptions.validateButton=Validar
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Edição Automática
@@ -1154,6 +1181,8 @@ licenses.license=Licença
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_ro_RO.properties b/src/main/resources/messages_ro_RO.properties
index f065256d678..d7dbffaa705 100644
--- a/src/main/resources/messages_ro_RO.properties
+++ b/src/main/resources/messages_ro_RO.properties
@@ -76,6 +76,7 @@ donate=Donează
color=Culoare
sponsor=Sponsor
info=Informații
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Descarcă
pipelineOptions.validateButton=Validează
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convertește din PDF
navbar.sections.security=Semnează & Securitate
navbar.sections.advance=Avansat
navbar.sections.edit=Vizualizează & Editează
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Fișierul nu a fost găsit
database.fileNullOrEmpty=Fișierul nu trebuie să fie nul sau gol
database.failedImportFile=Importul Fișierului a Eșuat
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Elimină imagine
home.removeImagePdf.desc=Elimină imaginea din PDF pentru a reduce dimensiunea fișierului
removeImagePdf.tags=Elimină Imagine,Operații pagină,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Acces Refuzat
login.oauth2InvalidTokenResponse=Răspuns Invalid la Token
login.oauth2InvalidIdToken=Token de Id Invalid
login.userIsDisabled=Utilizatorul este dezactivat, conectarea este în prezent blocată cu acest nume de utilizator. Te rugăm să contactezi administratorul.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Redactare Automată
@@ -1154,6 +1181,8 @@ licenses.license=Licență
survey.nav=Sondaj
survey.title=Sondaj Stirling-PDF
survey.description=Stirling-PDF nu are urmărire, așa că vrem să auzim de la utilizatorii noștri pentru a îmbunătăți Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Te rugăm să iei în considerare completarea sondajului nostru!
survey.disabled=(Fereastra pop-up a sondajului va fi dezactivată în următoarele actualizări, dar va fi disponibilă în subsolul paginii)
survey.button=Completează Sondajul
@@ -1179,3 +1208,17 @@ removeImage.title=Elimină imagine
removeImage.header=Elimină imagine
removeImage.removeImage=Elimină imagine
removeImage.submit=Elimină imagine
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_ru_RU.properties b/src/main/resources/messages_ru_RU.properties
index b5db3c478c5..d72c6feae0e 100644
--- a/src/main/resources/messages_ru_RU.properties
+++ b/src/main/resources/messages_ru_RU.properties
@@ -76,6 +76,7 @@ donate=Пожертвовать
color=Цвет
sponsor=Спонсор
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Конвейер:
pipelineOptions.saveButton=Скачать
pipelineOptions.validateButton=Проверить
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Автоматическое редактирование
@@ -1154,6 +1181,8 @@ licenses.license=Лицензия
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_sk_SK.properties b/src/main/resources/messages_sk_SK.properties
index d64aaeaf8ae..6e1518a3509 100644
--- a/src/main/resources/messages_sk_SK.properties
+++ b/src/main/resources/messages_sk_SK.properties
@@ -76,6 +76,7 @@ donate=Darovať
color=Farba
sponsor=Sponzorovať
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Stiahnuť
pipelineOptions.validateButton=Overiť
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Prispôsobiť veľkosť/škálovanie stránok
home.scalePages.desc=Zmeniť veľkosť/škálovanie stránky a/alebo jej obsahu.
scalePages.tags=veľkosť,modifikovať,rozmery,prispôsobiť
-home.pipeline.title=Pipeline (Pokročilé)
+home.pipeline.title=Pipeline
home.pipeline.desc=Spustiť viacero akcií na PDF definovaním pipeline skriptov
pipeline.tags=automatizovať,sekvencia,skriptované,dávkové spracovanie
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Automatické redigovanie
@@ -1154,6 +1181,8 @@ licenses.license=Licencia
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_sr_LATN_RS.properties b/src/main/resources/messages_sr_LATN_RS.properties
index 53bb42300a4..d1e68c51dcf 100644
--- a/src/main/resources/messages_sr_LATN_RS.properties
+++ b/src/main/resources/messages_sr_LATN_RS.properties
@@ -76,6 +76,7 @@ donate=Donate
color=Color
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Preuzmi
pipelineOptions.validateButton=Proveri
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto Cenzura
@@ -1154,6 +1181,8 @@ licenses.license=License
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_sv_SE.properties b/src/main/resources/messages_sv_SE.properties
index a85d294ce91..d9d8c0728e0 100644
--- a/src/main/resources/messages_sv_SE.properties
+++ b/src/main/resources/messages_sv_SE.properties
@@ -76,6 +76,7 @@ donate=Donera
color=Färg
sponsor=Sponsor
info=Info
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Ladda ner
pipelineOptions.validateButton=Validera
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Konvertera från PDF
navbar.sections.security=Signera & Säkerhet
navbar.sections.advance=Avancerat
navbar.sections.edit=Visa & Redigera
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Filen hittades inte
database.fileNullOrEmpty=Filen får inte vara null eller tom
database.failedImportFile=Misslyckades med att importera fil
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Ta bort bild
home.removeImagePdf.desc=Ta bort bild från PDF för att minska filstorlek
removeImagePdf.tags=Ta bort bild,Sidoperationer,Backend,serversida
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Åtkomst nekad
login.oauth2InvalidTokenResponse=Ogiltigt token-svar
login.oauth2InvalidIdToken=Ogiltigt Id-token
login.userIsDisabled=Användaren är inaktiverad, inloggning är för närvarande blockerad med detta användarnamn. Kontakta administratören.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Auto-redigera
@@ -1154,6 +1181,8 @@ licenses.license=Licens
survey.nav=Undersökning
survey.title=Stirling-PDF-undersökning
survey.description=Stirling-PDF har ingen spårning så vi vill höra från våra användare för att förbättra Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Vänligen överväg att delta i vår undersökning!
survey.disabled=(Undersökningspopup kommer att inaktiveras i kommande uppdateringar men finns tillgänglig längst ner på sidan)
survey.button=Delta i undersökningen
@@ -1179,3 +1208,17 @@ removeImage.title=Ta bort bild
removeImage.header=Ta bort bild
removeImage.removeImage=Ta bort bild
removeImage.submit=Ta bort bild
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_th_TH.properties b/src/main/resources/messages_th_TH.properties
index 69f93721a52..8df2f7fab1d 100644
--- a/src/main/resources/messages_th_TH.properties
+++ b/src/main/resources/messages_th_TH.properties
@@ -76,6 +76,7 @@ donate=บริจาค
color=สี
sponsor=ผู้สนับสนุน
info=ข้อมูล
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=ดาวน์โหลด
pipelineOptions.validateButton=ตรวจสอบความถูกต้อง
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=แปลงจาก PDF
navbar.sections.security=ลงนามและความปลอดภัย
navbar.sections.advance=ขั้นสูง
navbar.sections.edit=ดูและแก้ไข
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=ไม่พบไฟล์
database.fileNullOrEmpty=ไฟล์ต้องไม่ว่างเปล่าหรือไม่มีข้อมูล
database.failedImportFile=การนำเข้าไฟล์ล้มเหลว
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=การเข้าถึงถูกปฏิเส
login.oauth2InvalidTokenResponse=การตอบกลับโทเค็นไม่ถูกต้อง
login.oauth2InvalidIdToken=โทเค็น Id ไม่ถูกต้อง
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=ซ่อนข้อมูลอัตโนมัติ
@@ -1154,6 +1181,8 @@ licenses.license=ใบอนุญาต
survey.nav=สำรวจ
survey.title=สำรวจ Stirling-PDF
survey.description=Stirling-PDF ไม่มีการติดตาม ดังนั้นเราต้องการฟังความคิดเห็นจากผู้ใช้เพื่อปรับปรุง Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=กรุณาพิจารณาการสำรวจของเรา!
survey.disabled=(ป๊อปอัปการสำรวจจะถูกปิดใช้งานในการอัปเดตต่อไปนี้ แต่สามารถใช้ได้ที่ส่วนท้ายของหน้า)
survey.button=เริ่มสำรวจ
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_tr_TR.properties b/src/main/resources/messages_tr_TR.properties
index ad3a5339a39..ec95707c59d 100644
--- a/src/main/resources/messages_tr_TR.properties
+++ b/src/main/resources/messages_tr_TR.properties
@@ -76,6 +76,7 @@ donate=Bağış Yapın
color=Renk
sponsor=Bağış
info=Bilgi
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Çoklu İşlemler:
pipelineOptions.saveButton=İndir
pipelineOptions.validateButton=Doğrula
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=PDF'den dönüştür
navbar.sections.security=Oturum ve Güvenlik
navbar.sections.advance=Gelişmiş
navbar.sections.edit=Görüntüle ve Düzenle
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Dosya bulunamadı
database.fileNullOrEmpty=Dosya yok veya boş olmamalıdır
database.failedImportFile=Dosya İçe Aktarılamadı
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -382,7 +402,7 @@ home.scalePages.title=Sayfa boyutunu/ölçeğini ayarla
home.scalePages.desc=Bir sayfanın ve/veya içeriğinin boyutunu/ölçeğini değiştirir
scalePages.tags=boyutlandır,değiştir,boyut,uyarla
-home.pipeline.title=Çoklu İşlemler (İleri Seviye)
+home.pipeline.title=Çoklu İşlemler
home.pipeline.desc=Çoklu İşlemler tanımlayarak PDF'lere birden fazla işlemi çalıştır
pipeline.tags=otomatikleştir,sıralı,betikli,toplu-işlem
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Resmi kaldır
home.removeImagePdf.desc=Dosya boyutunu küçültmek için PDF'den resmi kaldırın
removeImagePdf.tags=Resmi Kaldır,Sayfa İşlemleri,Arka uç,sunucu tarafı
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Erişim Reddedildi
login.oauth2InvalidTokenResponse=Geçersiz Belirteç Yanıtı
login.oauth2InvalidIdToken=Geçersiz Kimlik Belirteci
login.userIsDisabled=Kullanıcı devre dışı bırakıldı, şu anda bu kullanıcı adıyla giriş engellendi. Lütfen yöneticiyle iletişime geçin.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Otomatik Karartma
@@ -1154,6 +1181,8 @@ licenses.license=Lisans
survey.nav=Anket
survey.title=Stirling-PDF Anketi
survey.description=Stirling-PDF'te izleme yok, bu yüzden Stirling-PDF'i iyileştirmek için kullanıcılarımızdan geri bildirim almak istiyoruz!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Lütfen anketimize katılmayı düşünün!
survey.disabled=(Anket açılır penceresi sonraki güncellemelerde devre dışı bırakılacak ancak sayfanın alt kısmında yer alacaktır)
survey.button=Ankete Katıl
@@ -1179,3 +1208,17 @@ removeImage.title=Resmi kaldır
removeImage.header=Resmi kaldır
removeImage.removeImage=Resmi kaldır
removeImage.submit=Resmi kaldır
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties
index ed35d4e6684..2a933bb78f4 100644
--- a/src/main/resources/messages_uk_UA.properties
+++ b/src/main/resources/messages_uk_UA.properties
@@ -76,6 +76,7 @@ donate=Задонатити
color=Колір
sponsor=Спонсор
info=Інформація
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Конвеєр:
pipelineOptions.saveButton=Завантажити
pipelineOptions.validateButton=Перевірити
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Конвертувати з PDF
navbar.sections.security=Підпис та Безпека
navbar.sections.advance=Додаткове
navbar.sections.edit=Перегляд та Редагування
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Доступ заблоковано
login.oauth2InvalidTokenResponse=Недійсна відповідь з токеном
login.oauth2InvalidIdToken=Недійсний Id токен
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Автоматичне редагування
@@ -1154,6 +1181,8 @@ licenses.license=Ліцензія
survey.nav=Опитування
survey.title=Опитування Stirling-PDF
survey.description=Stirling-PDF не має аналітичних засобів для відслідковування, тому ми хочемо почути думку від користувачів, як покращити Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Будь-ласка, пройдіть опитування!
survey.disabled=(Вікно з опитування буде відключено у наступних оновленнях, але буде доступне внизу сторінки)
survey.button=Пройти опитування
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_vi_VN.properties b/src/main/resources/messages_vi_VN.properties
index c12a40d530e..cb5de6aa9c5 100644
--- a/src/main/resources/messages_vi_VN.properties
+++ b/src/main/resources/messages_vi_VN.properties
@@ -76,6 +76,7 @@ donate=Ủng hộ
color=Màu sắc
sponsor=Nhà tài trợ
info=Thông tin
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=Pipeline:
pipelineOptions.saveButton=Tải xuống
pipelineOptions.validateButton=Xác thực
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=Chuyển đổi từ PDF
navbar.sections.security=Ký & Bảo mật
navbar.sections.advance=Nâng cao
navbar.sections.edit=Xem & Chỉnh sửa
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=Không tìm thấy tệp
database.fileNullOrEmpty=Tệp không được để trống hoặc rỗng
database.failedImportFile=Không thể nhập tệp
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=Truy cập bị từ chối
login.oauth2InvalidTokenResponse=Phản hồi token không hợp lệ
login.oauth2InvalidIdToken=Id Token không hợp lệ
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=Tự động biên tập
@@ -1154,6 +1181,8 @@ licenses.license=Giấy phép
survey.nav=Khảo sát
survey.title=Khảo sát Stirling-PDF
survey.description=Stirling-PDF không có cài đặt theo dõi nên chúng tôi muốn nghe từ người dùng để cải thiện Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=Vui lòng cân nhắc tham gia khảo sát của chúng tôi!
survey.disabled=(Cửa sổ popup khảo sát sẽ bị vô hiệu hóa trong các bản cập nhật tiếp theo nhưng vẫn tìm thấy ở cuối trang)
survey.button=Tham gia khảo sát
@@ -1179,3 +1208,17 @@ removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties
index 4ddf1e87c94..5a60398df79 100644
--- a/src/main/resources/messages_zh_CN.properties
+++ b/src/main/resources/messages_zh_CN.properties
@@ -76,6 +76,7 @@ donate=捐款
color=颜色
sponsor=赞助
info=信息
+pro=Pro
page=Page
pages=Pages
@@ -110,8 +111,24 @@ pipelineOptions.pipelineHeader=流水线:
pipelineOptions.saveButton=下载
pipelineOptions.validateButton=验证
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=Upgrade to Pro
+enterpriseEdition.warning=This feature is only available to Pro users.
+enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
+enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
+
+
+#################
+# Analytics #
+#################
+analytics.title=Do you want make Stirling PDF better?
+analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
+analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
+analytics.enable=Enable analytics
+analytics.disable=Disable analytics
+analytics.settings=You can change the settings for analytics in the config/settings.yml file
#############
# NAVBAR #
@@ -128,6 +145,7 @@ navbar.sections.convertFrom=从PDF转换
navbar.sections.security=签名和安全
navbar.sections.advance=高级功能
navbar.sections.edit=查看和编辑
+navbar.sections.popular=Popular
#############
# SETTINGS #
@@ -225,6 +243,8 @@ database.fileNotFound=未找到文件
database.fileNullOrEmpty=文件不能为空
database.failedImportFile=导入文件失败
+session.expired=Your session has expired. Please refresh the page and try again.
+
#############
# HOME-PAGE #
#############
@@ -482,6 +502,11 @@ home.removeImagePdf.title=删除图像
home.removeImagePdf.desc=删除图像减少PDF大小
removeImagePdf.tags=删除图像, 页面操作, 后端, 服务端
+
+home.splitPdfByChapters.title=Split PDF by Chapters
+home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
+splitPdfByChapters.tags=split,chapters,bookmarks,organize
+
#replace-invert-color
replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF
@@ -526,7 +551,9 @@ login.oauth2AccessDenied=拒绝访问
login.oauth2InvalidTokenResponse=无效的Token响应
login.oauth2InvalidIdToken=无效的Token
login.userIsDisabled=用户被禁用,登录已被阻止。请联系管理员。
-
+login.alreadyLoggedIn=You are already logged in to
+login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
+login.toManySessions=You have too many active sessions
#auto-redact
autoRedact.title=自动删除
@@ -1154,6 +1181,8 @@ licenses.license=许可证
survey.nav=调查
survey.title=Stirling-PDF调查
survey.description=Stirling-PDF没有跟踪器,所以我们希望听取用户的意见来改进Stirling-PDF!
+survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
+survey.changes2=With these changes we are getting paid business support and funding
survey.please=请考虑参加我们的调查!
survey.disabled=(调查弹出窗口将在后续更新中被禁用,但可在页脚处查看)
survey.button=参与调查
@@ -1179,3 +1208,17 @@ removeImage.title=删除图像
removeImage.header=删除图像
removeImage.removeImage=删除图像
removeImage.submit=删除图像
+
+
+splitByChapters.title=Split PDF by Chapters
+splitByChapters.header=Split PDF by Chapters
+splitByChapters.bookmarkLevel=Bookmark Level
+splitByChapters.includeMetadata=Include Metadata
+splitByChapters.allowDuplicates=Allow Duplicates
+splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
+splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
+splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
+splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
+splitByChapters.submit=Split PDF
+
+
diff --git a/src/main/resources/messages_zh_TW.properties b/src/main/resources/messages_zh_TW.properties
index 9e912b554b3..cf23acfbbef 100644
--- a/src/main/resources/messages_zh_TW.properties
+++ b/src/main/resources/messages_zh_TW.properties
@@ -3,8 +3,8 @@
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
-addPageNumbers.fontSize=Font Size
-addPageNumbers.fontName=Font Name
+addPageNumbers.fontSize=字型大小
+addPageNumbers.fontName=字型名稱
pdfPrompt=選擇 PDF 檔案
multiPdfPrompt=選擇多個 PDF 檔案
multiPdfDropPrompt=選擇(或拖放)所有需要的 PDF 檔案
@@ -12,7 +12,7 @@ imgPrompt=選擇圖片
genericSubmit=送出
processTimeWarning=警告:此過程可能長達一分鐘,具體取決於檔案大小
pageOrderPrompt=自訂頁面順序(輸入以逗號分隔的頁碼或函式,如 2n+1):
-pageSelectionPrompt=自訂頁面選擇(輸入以逗號分隔的頁碼 1、5、6 或 2n+1 等函數的清單):
+pageSelectionPrompt=自訂頁面選擇(輸入以逗號分隔的頁碼 1、5、6 或 2n+1 等函式的清單):
goToPage=前往
true=是
false=否
@@ -21,7 +21,7 @@ save=儲存
saveToBrowser=儲存到瀏覽器
close=關閉
filesSelected=已選擇的檔案
-noFavourites=未新增收藏
+noFavourites=還沒有功能被收藏
downloadComplete=下載完成
bored=等待時覺得無聊?
alphabet=字母表
@@ -51,67 +51,84 @@ poweredBy=Powered by
yes=是
no=否
changedCredsMessage=憑證已變更!
-notAuthenticatedMessage=使用者未認證。
+notAuthenticatedMessage=使用者未通過驗證。
userNotFoundMessage=找不到使用者。
incorrectPasswordMessage=目前密碼不正確。
usernameExistsMessage=新使用者名稱已存在。
-invalidUsernameMessage=使用者名稱無效,使用者名稱只能包含字母、數字和以下特殊字元 @._+- 或必須是有效的電子郵件地址。
-invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
-confirmPasswordErrorMessage=輸入的密碼必需和確認密碼相同。
-deleteCurrentUserMessage=無法刪除目前登錄的使用者。
-deleteUsernameExistsMessage=使用者名不存在,無法刪除。
+invalidUsernameMessage=無效的使用者名稱。使用者名稱只能包含字母、數字和以下特殊字元 @._+- 或必須是有效的電子郵件地址。
+invalidPasswordMessage=密碼不能為空,且開頭和結尾不能有空格。
+confirmPasswordErrorMessage=新密碼與確認新密碼必須相符。
+deleteCurrentUserMessage=無法刪除目前登入的使用者。
+deleteUsernameExistsMessage=使用者名稱不存在,無法刪除。
downgradeCurrentUserMessage=無法降級目前使用者的角色
-disabledCurrentUserMessage=The current user cannot be disabled
-downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。
-userAlreadyExistsOAuthMessage=該使用者已於 OAuth2 註冊。
-userAlreadyExistsWebMessage=該使用者已於網頁註冊。
+disabledCurrentUserMessage=無法停用目前使用者
+downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,將不會顯示目前使用者。
+userAlreadyExistsOAuthMessage=使用者已經以 OAuth2 使用者身份存在。
+userAlreadyExistsWebMessage=使用者已經以網頁使用者身份存在。
error=錯誤
oops=哎呀!
-help=幫助
+help=說明
goHomepage=前往首頁
joinDiscord=加入我們的 Discord 伺服器
-seeDockerHub=查看 Docker Hub
-visitGithub=訪問 GitHub 存儲庫
-donate=捐贈
+seeDockerHub=造訪 Docker Hub 儲存庫
+visitGithub=造訪 GitHub 專案
+donate=捐款
color=顏色
sponsor=贊助
-info=Info
-page=Page
-pages=Pages
+info=資訊
+pro=專業版
+page=頁面
+pages=頁面
-legal.privacy=Privacy Policy
-legal.terms=Terms and Conditions
-legal.accessibility=Accessibility
-legal.cookie=Cookie Policy
-legal.impressum=Impressum
+legal.privacy=隱私權政策
+legal.terms=使用條款
+legal.accessibility=無障礙性聲明
+legal.cookie=Cookie 政策
+legal.impressum=版本說明
###############
# Pipeline #
###############
-pipeline.header=管道選單(測試版)
-pipeline.uploadButton=上傳自定義
-pipeline.configureButton=配置
+pipeline.header=管道功能選單(測試版)
+pipeline.uploadButton=上傳自訂設定
+pipeline.configureButton=設定
pipeline.defaultOption=自訂
pipeline.submitButton=送出
-pipeline.help=管道説明
-pipeline.scanHelp=資料夾掃描説明
-pipeline.deletePrompt=確定刪除該管道?
+pipeline.help=管道功能說明
+pipeline.scanHelp=資料夾掃描說明
+pipeline.deletePrompt=您確定要刪除此管道嗎?
######################
# Pipeline Options #
######################
-pipelineOptions.header=管道配置
+pipelineOptions.header=管道設定
pipelineOptions.pipelineNameLabel=管道名稱
-pipelineOptions.saveSettings=保存操作設置
-pipelineOptions.pipelineNamePrompt=在此處輸入管道名稱
+pipelineOptions.saveSettings=儲存操作設定
+pipelineOptions.pipelineNamePrompt=請在此輸入管道名稱
pipelineOptions.selectOperation=選擇操作
-pipelineOptions.addOperationButton=添加操作
+pipelineOptions.addOperationButton=新增操作
pipelineOptions.pipelineHeader=管道:
pipelineOptions.saveButton=下載
pipelineOptions.validateButton=驗證
-
-
+########################
+# ENTERPRISE EDITION #
+########################
+enterpriseEdition.button=升級至專業版
+enterpriseEdition.warning=此功能僅提供給專業版使用者使用。
+enterpriseEdition.yamlAdvert=Stirling PDF 專業版支援 YAML 設定檔和其他單一登入 (SSO) 功能。
+enterpriseEdition.ssoAdvert=需要更多使用者管理功能嗎?請參考 Stirling PDF 專業版
+
+
+#################
+# Analytics #
+#################
+analytics.title=您想協助改善 Stirling PDF 嗎?
+analytics.paragraph1=Stirling PDF 有選擇性的分析功能,可幫助我們改進產品。我們不會追蹤任何個人資訊或檔案內容。
+analytics.paragraph2=請考慮啟用分析功能,以協助 Stirling-PDF 成長並讓我們更了解使用者需求。
+analytics.enable=啟用分析功能
+analytics.disable=停用分析功能
+analytics.settings=您可以在 config/settings.yml 檔案中變更分析功能的設定
#############
# NAVBAR #
@@ -121,35 +138,36 @@ navbar.darkmode=深色模式
navbar.language=語言
navbar.settings=設定
navbar.allTools=工具
-navbar.multiTool=萬用工具
-navbar.sections.organize=組織
+navbar.multiTool=多功能工具
+navbar.sections.organize=整理
navbar.sections.convertTo=轉換為 PDF
navbar.sections.convertFrom=從 PDF 轉換
navbar.sections.security=簽章與安全性
navbar.sections.advance=進階
navbar.sections.edit=檢視與編輯
+navbar.sections.popular=熱門功能
#############
# SETTINGS #
#############
settings.title=設定
settings.update=有更新可用
-settings.updateAvailable=當前版本為 {0}。歡迎你更新至最新版 ({1})。。
-settings.appVersion=應用版本:
-settings.downloadOption.title=選擇下載選項(對於單一檔案非壓縮下載):
+settings.updateAvailable=目前安裝的版本是 {0}。有新版本({1})可供使用。
+settings.appVersion=應用程式版本:
+settings.downloadOption.title=選擇下載選項(適用於單一檔案非壓縮下載):
settings.downloadOption.1=在同一視窗中開啟
settings.downloadOption.2=在新視窗中開啟
settings.downloadOption.3=下載檔案
-settings.zipThreshold=當下載的檔案數量超過時,壓縮檔案
+settings.zipThreshold=當下載檔案數量超過此數值時,將檔案壓縮
settings.signOut=登出
-settings.accountSettings=帳戶設定
-settings.bored.help=啟用彩蛋
-settings.cacheInputs.name=輸入檔案下載
-settings.cacheInputs.help=開啟記住先前的輸入,做為日後使用
+settings.accountSettings=帳號設定
+settings.bored.help=啟用彩蛋遊戲
+settings.cacheInputs.name=儲存表單輸入
+settings.cacheInputs.help=啟用此功能以儲存先前使用的輸入,以便日後使用
changeCreds.title=變更憑證
-changeCreds.header=更新你的帳戶詳細資訊
-changeCreds.changePassword=你使用的是預設登錄認證。請輸入新密碼
+changeCreds.header=更新您的帳號詳細資訊
+changeCreds.changePassword=您正在使用預設登入憑證。請輸入新密碼
changeCreds.newUsername=新使用者名稱
changeCreds.oldPassword=目前密碼
changeCreds.newPassword=新密碼
@@ -158,9 +176,9 @@ changeCreds.submit=送出變更
-account.title=帳戶設定
-account.accountSettings=帳戶設定
-account.adminSettings=管理設定 - 檢視和新增使用者
+account.title=帳號設定
+account.accountSettings=帳號設定
+account.adminSettings=管理員設定 - 檢視和新增使用者
account.userControlSettings=使用者控制設定
account.changeUsername=修改使用者名稱
account.newUsername=新使用者名稱
@@ -170,43 +188,43 @@ account.newPassword=新密碼
account.changePassword=修改密碼
account.confirmNewPassword=確認新密碼
account.signOut=登出
-account.yourApiKey=你的 API 金鑰
-account.syncTitle=將瀏覽器設定與帳戶同步
+account.yourApiKey=您的 API 金鑰
+account.syncTitle=將瀏覽器設定與帳號同步
account.settingsCompare=設定比較:
account.property=屬性
account.webBrowserSettings=網頁瀏覽器設定
-account.syncToBrowser=同步帳戶 → 瀏覽器
-account.syncToAccount=同步帳戶 ← 瀏覽器
+account.syncToBrowser=同步帳號 → 瀏覽器
+account.syncToAccount=同步帳號 ← 瀏覽器
adminUserSettings.title=使用者控制設定
-adminUserSettings.header=管理使用者控制設定
+adminUserSettings.header=管理員使用者控制設定
adminUserSettings.admin=管理員
adminUserSettings.user=使用者
adminUserSettings.addUser=新增使用者
adminUserSettings.deleteUser=刪除使用者
-adminUserSettings.confirmDeleteUser=確認刪除該使用者?
-adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
-adminUserSettings.usernameInfo=使用者名稱只能包含字母、數字和以下特殊字元 @._+-,或必須是有效的電子郵件地址。
+adminUserSettings.confirmDeleteUser=確定要刪除此使用者?
+adminUserSettings.confirmChangeUserStatus=是否要停用/啟用此使用者?
+adminUserSettings.usernameInfo=使用者名稱只能包含字母、數字和以下特殊字元 @._+- 或必須是有效的電子郵件地址。
adminUserSettings.roles=角色
adminUserSettings.role=角色
adminUserSettings.actions=操作
adminUserSettings.apiUser=受限制的 API 使用者
-adminUserSettings.extraApiUser=其他受限 API 使用者
-adminUserSettings.webOnlyUser=僅使用網頁的使用者
-adminUserSettings.demoUser=示範用途的使用者(無自訂設定)
+adminUserSettings.extraApiUser=額外受限制的 API 使用者
+adminUserSettings.webOnlyUser=僅網頁版使用者
+adminUserSettings.demoUser=示範使用者(無自訂設定)
adminUserSettings.internalApiUser=內部 API 使用者
-adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼
-adminUserSettings.submit=儲存
-adminUserSettings.changeUserRole=更改使用者身份
+adminUserSettings.forceChange=強制使用者在登入時變更密碼
+adminUserSettings.submit=儲存使用者
+adminUserSettings.changeUserRole=變更使用者角色
adminUserSettings.authenticated=已驗證
-adminUserSettings.editOwnProfil=Edit own profile
-adminUserSettings.enabledUser=enabled user
-adminUserSettings.disabledUser=disabled user
-adminUserSettings.activeUsers=Active Users:
-adminUserSettings.disabledUsers=Disabled Users:
-adminUserSettings.totalUsers=Total Users:
-adminUserSettings.lastRequest=Last Request
+adminUserSettings.editOwnProfil=編輯自己的個人資料
+adminUserSettings.enabledUser=已啟用使用者
+adminUserSettings.disabledUser=已停用使用者
+adminUserSettings.activeUsers=使用中的使用者:
+adminUserSettings.disabledUsers=已停用的使用者:
+adminUserSettings.totalUsers=使用者總數:
+adminUserSettings.lastRequest=最後請求時間
database.title=資料庫匯入/匯出
@@ -217,18 +235,20 @@ database.fileSize=檔案大小
database.deleteBackupFile=刪除備份檔案
database.importBackupFile=匯入備份檔案
database.downloadBackupFile=下載備份檔案
-database.info_1=在匯入資料時,確保正確的結構是至關重要的。如果你不確定自己在做什麼,請尋求專業人士的建議和支持。結構錯誤可能會導致應用程式故障,甚至完全無法運行。
-database.info_2=上傳時檔案名稱無關緊要。上傳後將重新命名為 backup_user_yyyyMMddHHmm.sql 格式,以確保命名規範一致。
+database.info_1=在匯入資料時,確保正確的結構至關重要。如果您不確定自己在做什麼,請尋求專業人士的建議和支援。結構錯誤可能會導致應用程式故障,甚至完全無法執行應用程式。
+database.info_2=上傳時檔案名稱並不重要。上傳後將重新命名為 backup_user_yyyyMMddHHmm.sql 格式,以確保命名規範一致。
database.submit=匯入備份
database.importIntoDatabaseSuccessed=成功匯入資料庫
-database.fileNotFound=檔案未找到
-database.fileNullOrEmpty=檔案不能為空或空白
+database.fileNotFound=找不到檔案
+database.fileNullOrEmpty=檔案不得為空或空白
database.failedImportFile=匯入檔案失敗
+session.expired=您的工作階段已過期。請重新整理頁面並再試一次。
+
#############
# HOME-PAGE #
#############
-home.desc=你的本地主機一站式 PDF 需求解決方案。
+home.desc=你的本機主機一站式 PDF 需求解決方案。
home.searchBar=搜尋功能...
@@ -236,9 +256,9 @@ home.viewPdf.title=檢視 PDF
home.viewPdf.desc=檢視、註釋、新增文字或圖片
viewPdf.tags=檢視,閱讀,註釋,文字,圖片
-home.multiTool.title=PDF 多工具
+home.multiTool.title=PDF 複合工具
home.multiTool.desc=合併、旋轉、重新排列和移除頁面
-multiTool.tags=多工具,多操作,UI,點選拖動,前端,客戶端,互動,可互動,移動
+multiTool.tags=複合工具,多功能,UI,點選拖曳,前端,客戶端,互動,互動式,移動
home.merge.title=合併
home.merge.desc=輕鬆將多個 PDF 合併為一個。
@@ -372,7 +392,7 @@ certSign.tags=驗證,PEM,P12,官方,加密
home.removeCertSign.title=移除簽章
home.removeCertSign.desc=從 PDF 移除簽章
-removeCertSign.tags=authenticate,PEM,P12,official,decrypt
+removeCertSign.tags=驗證,PEM,P12,官方,解密
home.pageLayout.title=多頁面版面配置
home.pageLayout.desc=將 PDF 檔案的多個頁面合併到單一頁面
@@ -465,9 +485,9 @@ home.split-by-sections.title=依區段分割 PDF
home.split-by-sections.desc=將 PDF 的每一頁分割為較小的水平和垂直區段
split-by-sections.tags=區段分割, 劃分, 自訂
-home.AddStampRequest.title=將圖章添加到 PDF
-home.AddStampRequest.desc=在設置位置添加文字或添加圖像圖章
-AddStampRequest.tags=圖章,添加圖片,中心圖像,浮水印,PDF,嵌入,自訂
+home.AddStampRequest.title=將圖章新增到 PDF
+home.AddStampRequest.desc=在設定位置新增文字或新增影像圖章
+AddStampRequest.tags=圖章,新增圖片,中心影像,浮水印,PDF,嵌入,自訂
home.PDFToBook.title=PDF 轉電子書
@@ -478,28 +498,33 @@ home.BookToPDF.title=電子書轉 PDF
home.BookToPDF.desc=使用 calibre 將書籍/漫畫格式轉換為 PDF
BookToPDF.tags=電子書,漫畫,Calibre,轉換,日本漫畫,亞馬遜,kindle
-home.removeImagePdf.title=Remove image
-home.removeImagePdf.desc=Remove image from PDF to reduce file size
-removeImagePdf.tags=Remove Image,Page operations,Back end,server side
+home.removeImagePdf.title=移除圖片
+home.removeImagePdf.desc=從 PDF 中移除圖片以減少檔案大小
+removeImagePdf.tags=移除圖片,頁面操作,後端,伺服器端
+
+
+home.splitPdfByChapters.title=依章節分割 PDF
+home.splitPdfByChapters.desc=根據 PDF 的章節結構將其分割成多個檔案。
+splitPdfByChapters.tags=分割,章節,書籤,整理
#replace-invert-color
-replace-color.title=Replace-Invert-Color
-replace-color.header=Replace-Invert Color PDF
-home.replaceColorPdf.title=Replace and Invert Color
-home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
-replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
-replace-color.selectText.1=Replace or Invert color Options
-replace-color.selectText.2=Default(Default high contrast colors)
-replace-color.selectText.3=Custom(Customized colors)
-replace-color.selectText.4=Full-Invert(Invert all colors)
-replace-color.selectText.5=High contrast color options
-replace-color.selectText.6=white text on black background
-replace-color.selectText.7=Black text on white background
-replace-color.selectText.8=Yellow text on black background
-replace-color.selectText.9=Green text on black background
-replace-color.selectText.10=Choose text Color
-replace-color.selectText.11=Choose background Color
-replace-color.submit=Replace
+replace-color.title=取代-反轉顏色
+replace-color.header=取代-反轉 PDF 顏色
+home.replaceColorPdf.title=取代與反轉顏色
+home.replaceColorPdf.desc=取代 PDF 中文字和背景的顏色,並反轉整個 PDF 的顏色以減少檔案大小
+replaceColorPdf.tags=取代顏色,頁面操作,後端,伺服器端
+replace-color.selectText.1=取代或反轉顏色選項
+replace-color.selectText.2=預設(預設高對比度顏色)
+replace-color.selectText.3=自訂(自訂顏色)
+replace-color.selectText.4=全部反轉(反轉所有顏色)
+replace-color.selectText.5=高對比度顏色選項
+replace-color.selectText.6=黑底白字
+replace-color.selectText.7=白底黑字
+replace-color.selectText.8=黑底黃字
+replace-color.selectText.9=黑底綠字
+replace-color.selectText.10=選擇文字顏色
+replace-color.selectText.11=選擇背景顏色
+replace-color.submit=取代
@@ -517,16 +542,18 @@ login.invalid=使用者名稱或密碼無效。
login.locked=你的帳戶已被鎖定。
login.signinTitle=請登入
login.ssoSignIn=透過織網單一簽入
-login.oauth2AutoCreateDisabled=OAuth 2.0 自動建立使用者已停用
-login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
+login.oauth2AutoCreateDisabled=OAuth 2.0 自動建立使用者功能已停用
+login.oauth2AdminBlockedUser=目前已封鎖非註冊使用者的註冊或登入。請聯絡系統管理員。
login.oauth2RequestNotFound=找不到驗證請求
login.oauth2InvalidUserInfoResponse=無效的使用者資訊回應
login.oauth2invalidRequest=無效的回應
login.oauth2AccessDenied=存取被拒
-login.oauth2InvalidTokenResponse=無效的 Token 回應
-login.oauth2InvalidIdToken=無效的 Tokne
-login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
-
+login.oauth2InvalidTokenResponse=無效的權杖回應
+login.oauth2InvalidIdToken=無效的識別權杖
+login.userIsDisabled=使用者已停用,目前此使用者無法登入。請聯絡系統管理員。
+login.alreadyLoggedIn=您已經登入了
+login.alreadyLoggedIn2=個裝置。請登出其他裝置後再試一次。
+login.toManySessions=您有太多使用中的工作階段
#auto-redact
autoRedact.title=自動塗黑
@@ -612,7 +639,7 @@ AddStampRequest.stampType=圖章類型
AddStampRequest.stampText=圖章文字
AddStampRequest.stampImage=圖章圖片
AddStampRequest.alphabet=字母表
-AddStampRequest.fontSize=字體/圖像大小
+AddStampRequest.fontSize=字型/影像大小
AddStampRequest.rotation=旋轉
AddStampRequest.opacity=透明度
AddStampRequest.position=位置
@@ -701,7 +728,7 @@ pageLayout.submit=送出
scalePages.title=調整頁面大小/比例
scalePages.header=調整頁面大小/比例
scalePages.pageSize=文件的頁面大小。
-scalePages.keepPageSize=Original Size
+scalePages.keepPageSize=原始大小
scalePages.scaleFactor=頁面的縮放級別(裁剪)。
scalePages.submit=送出
@@ -710,7 +737,7 @@ scalePages.submit=送出
certSign.title=憑證簽章
certSign.header=使用你的憑證簽章(進行中)
certSign.selectPDF=選擇要簽章的 PDF 檔案:
-certSign.jksNote=注意:如果你的證書類型未在下面列出,請使用 keytool 命令行工具將其轉換為 Java Keystore (.jks) 檔。 然後,選擇下面的 .jks 文件選項。
+certSign.jksNote=注意:如果你的證書類型未在下面列出,請使用 keytool 命令列工具將其轉換為 Java Keystore (.jks) 檔。 然後,選擇下面的 .jks 文件選項。
certSign.selectKey=選擇你的私鑰文件(PKCS#8 格式,可能是 .pem 或 .der):
certSign.selectCert=選擇你的憑證文件(X.509 格式,可能是 .pem 或 .der):
certSign.selectP12=選擇你的 PKCS#12 金鑰庫文件(.p12 或 .pfx)(可選,如果提供,它應包含你的私鑰和憑證):
@@ -750,8 +777,8 @@ removeAnnotations.submit=移除
#compare
compare.title=比較
compare.header=比較 PDF
-compare.highlightColor.1=Highlight Color 1:
-compare.highlightColor.2=Highlight Color 2:
+compare.highlightColor.1=標示顏色 1:
+compare.highlightColor.2=標示顏色 2:
compare.document.1=文件 1
compare.document.2=文件 2
compare.submit=比較
@@ -788,7 +815,7 @@ repair.submit=修復
#flatten
flatten.title=平坦化
flatten.header=PDF 平坦化
-flatten.flattenOnlyForms=Flatten only forms
+flatten.flattenOnlyForms=僅將表單平坦化
flatten.submit=平坦化
@@ -803,7 +830,7 @@ ScannerImageSplit.selectText.7=最小輪廓區域:
ScannerImageSplit.selectText.8=設定照片的最小輪廓區域閾值
ScannerImageSplit.selectText.9=邊框大小:
ScannerImageSplit.selectText.10=設定新增和移除的邊框大小,以防止輸出中的白色邊框(預設:1)。
-ScannerImageSplit.info=Python is not installed. It is required to run.
+ScannerImageSplit.info=尚未安裝 Python。需要安裝 Python 才能執行。
#OCR
@@ -812,8 +839,8 @@ ocr.header=清理掃描 / OCR(光學字元識別)
ocr.selectText.1=選擇要在 PDF 中偵測的語言(列出的是目前可以偵測的語言):
ocr.selectText.2=產生包含 OCR 文字的文字文件,並與 OCR 的 PDF 一起
ocr.selectText.3=修正掃描的頁面傾斜角度,將它們旋轉回原位
-ocr.selectText.4=清理頁面,使 OCR 不太可能在背景噪音中找到文字。(無輸出變化)
-ocr.selectText.5=清理頁面,使 OCR 不太可能在背景噪音中找到文字,保持清理的輸出。
+ocr.selectText.4=清理頁面以降低 OCR 在背景雜訊中識別文字的機率。(無輸出變化)
+ocr.selectText.5=清理頁面以降低 OCR 在背景雜訊中識別文字的機率,保持乾淨的輸出。
ocr.selectText.6=忽略具有互動文字的頁面,只對影像頁面進行 OCR
ocr.selectText.7=強制 OCR,將對每一頁進行 OCR,移除所有原始文字元素
ocr.selectText.8=正常(如果 PDF 包含文字將出錯)
@@ -830,7 +857,7 @@ ocr.submit=使用 OCR 處理 PDF
extractImages.title=提取圖片
extractImages.header=提取圖片
extractImages.selectText=選擇要轉換提取影像的影像格式
-extractImages.allowDuplicates=Save duplicate images
+extractImages.allowDuplicates=儲存重複的圖片
extractImages.submit=提取
@@ -848,10 +875,10 @@ compress.title=壓縮
compress.header=壓縮 PDF
compress.credit=此服務使用 Ghostscript 進行 PDF 壓縮/最佳化。
compress.selectText.1=手動模式 - 從 1 到 4
-compress.selectText.2=最佳化級別:
-compress.selectText.3=4(對於文字影像非常糟糕)
-compress.selectText.4=自動模式 - 自動調整品質以使 PDF 達到確定大小
-compress.selectText.5=預期的 PDF 大小(例如 25MB, 10.8MB, 25KB)
+compress.selectText.2=最佳化等級:
+compress.selectText.3=4(對於含有文字的影像來說結果很糟)
+compress.selectText.4=自動模式 - 自動調整品質使 PDF 達到指定的檔案大小
+compress.selectText.5=指定的 PDF 檔案大小(例如 25MB, 10.8MB, 25KB)
compress.submit=壓縮
@@ -886,14 +913,14 @@ pdfOrganiser.mode.6=奇偶拆分
pdfOrganiser.mode.7=刪除第一頁
pdfOrganiser.mode.8=刪除最後一頁
pdfOrganiser.mode.9=刪除第一頁和最後一頁
-pdfOrganiser.mode.10=Odd-Even Merge
+pdfOrganiser.mode.10=奇偶合併
pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1)
#multiTool
-multiTool.title=PDF 多工具
-multiTool.header=PDF 多工具
-multiTool.uploadPrompts=File Name
+multiTool.title=PDF 複合工具
+multiTool.header=PDF 複合工具
+multiTool.uploadPrompts=檔名
#view pdf
viewPdf.title=檢視 PDF
@@ -955,7 +982,7 @@ pdfToImage.color=顏色
pdfToImage.grey=灰度
pdfToImage.blackwhite=黑白(可能會遺失資料!)
pdfToImage.submit=轉換
-pdfToImage.info=Python is not installed. Required for WebP conversion.
+pdfToImage.info=尚未安裝 Python。需要安裝 Python 才能進行 WebP 轉換。
#addPassword
@@ -992,7 +1019,7 @@ watermark.selectText.6=heightSpacer(每個浮水印之間的垂直間距):
watermark.selectText.7=不透明度(0% - 100%):
watermark.selectText.8=浮水印類型:
watermark.selectText.9=浮水印影像:
-watermark.selectText.10=Convert PDF to PDF-Image
+watermark.selectText.10=將 PDF 轉換為 PDF 影像
watermark.submit=新增浮水印
watermark.type.1=文字
watermark.type.2=圖片
@@ -1143,39 +1170,55 @@ printFile.submit=列印
#licenses
-licenses.nav=許可證
-licenses.title=第三方許可證
-licenses.header=第三方許可證
+licenses.nav=授權條款
+licenses.title=第三方授權條款
+licenses.header=第三方授權條款
licenses.module=模組
licenses.version=版本
-licenses.license=許可證
+licenses.license=授權條款
#survey
survey.nav=問卷調查
survey.title=Stirling-PDF 問卷調查
-survey.description=Stirling-PDF 沒有追蹤功能,所以我們希望聽取用戶的意見來改進 Stirling-PDF!
-survey.please=請考慮參加我們的問卷調查!
-survey.disabled=(問卷調查彈出窗口將在後續更新中停用,但仍可在頁腳處使用)
-survey.button=參加問卷調查
+survey.description=Stirling-PDF 沒有追蹤功能,因此我們希望聽取使用者的意見來改進 Stirling-PDF!
+survey.changes=Stirling-PDF 自上次調查以來已有所改變!欲了解更多資訊,請查看我們的部落格文章:
+survey.changes2=隨著這些變更,我們正在獲得付費的商業支援和資金
+survey.please=請考慮參與我們的問卷調查!
+survey.disabled=(問卷調查彈出視窗將在後續更新中停用,但仍可在頁尾使用)
+survey.button=參與問卷調查
survey.dontShowAgain=不要再次顯示
#error
-error.sorry=對於這個問題,我們感到抱歉!
-error.needHelp=需要幫助/發現了一個問題?
-error.contactTip=如果你仍然遇到問題,請不要猶豫,隨時向我們尋求幫助。你可以在我們的 GitHub 頁面提交工單,或通過 Discord 與我們聯繋:
-error.404.head=404 - 找不到頁面 | 哎呀,我們在代碼中走錯了路!
-error.404.1=我們好像找不到你正在尋找的頁面。
-error.404.2=出了點錯誤
-error.github=在 GitHub 上提交工單
+error.sorry=很抱歉造成您的困擾!
+error.needHelp=需要協助或發現問題?
+error.contactTip=如果您仍然遇到問題,請不要猶豫,隨時向我們尋求協助。您可以在我們的 GitHub 頁面回報問題,或透過 Discord 跟我們聯絡:
+error.404.head=404 - 找不到頁面 | 糟糕,我們在程式碼中迷路了!
+error.404.1=我們似乎找不到您正在尋找的頁面。
+error.404.2=發生了一些錯誤
+error.github=在 GitHub 上回報問題
error.showStack=顯示堆疊追蹤
error.copyStack=複製堆疊追蹤
-error.githubSubmit=GitHub - 提交工單
-error.discordSubmit=Discord - 提交支援帖子
+error.githubSubmit=GitHub - 回報問題
+error.discordSubmit=Discord - 發表支援文章
#remove-image
-removeImage.title=Remove image
-removeImage.header=Remove image
-removeImage.removeImage=Remove image
-removeImage.submit=Remove image
+removeImage.title=移除圖片
+removeImage.header=移除圖片
+removeImage.removeImage=移除圖片
+removeImage.submit=移除圖片
+
+
+splitByChapters.title=依章節分割 PDF
+splitByChapters.header=依章節分割 PDF
+splitByChapters.bookmarkLevel=書籤層級
+splitByChapters.includeMetadata=包含中繼資料
+splitByChapters.allowDuplicates=允許重複
+splitByChapters.desc.1=此工具會根據 PDF 檔案的章節結構將其分割成多個 PDF。
+splitByChapters.desc.2=書籤層級:選擇用於分割的書籤層級(0 表示最上層,1 表示第二層,依此類推)。
+splitByChapters.desc.3=包含中繼資料:如果勾選,原始 PDF 的中繼資料將包含在每個分割後的 PDF 中。
+splitByChapters.desc.4=允許重複:如果勾選,允許同一頁面上的多個書籤建立獨立的 PDF。
+splitByChapters.submit=分割 PDF
+
+
diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template
index 2a5400b6018..0381d2cf396 100644
--- a/src/main/resources/settings.yml.template
+++ b/src/main/resources/settings.yml.template
@@ -47,19 +47,31 @@ security:
useAsUsername: email # Default is 'email'; custom fields can be used as the username
scopes: openid, profile, email # Specify the scopes for which the application will request permissions
provider: google # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
+ saml2:
+ enabled: false # Currently in alpha, not recommended for use yet, enableAlphaFunctionality must be set to true
+ autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
+ blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
+ registrationId: stirling
+ idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata
+ idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml
+ idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml
+ idpIssuer: http://www.okta.com/externalKey
+ idpCert: classpath:octa.crt
+ privateKey: classpath:saml-private-key.key
+ spCert: classpath:saml-public-cert.crt
-# Enterprise edition settings unused for now please ignore!
-EnterpriseEdition:
+enterpriseEdition:
+ enabled: false # set to 'true' to enable enterprise edition
key: 00000000-0000-0000-0000-000000000000
CustomMetadata:
- autoUpdateMetadata: true # set to 'true' to automatically update metadata with below values
- author: username # Supports text such as 'John Doe' or types such as username
+ autoUpdateMetadata: false # set to 'true' to automatically update metadata with below values
+ author: username # Supports text such as 'John Doe' or types such as username to autopopulate with users username
creator: Stirling-PDF # Supports text such as 'Company-PDF'
producer: Stirling-PDF # Supports text such as 'Company-PDF'
legal:
- termsAndConditions: '' # URL to the terms and conditions of your application (e.g. https://example.com/terms) Empty string to disable or filename to load from local file in static folder
- privacyPolicy: '' # URL to the privacy policy of your application (e.g. https://example.com/privacy) Empty string to disable or filename to load from local file in static folder
+ termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms) Empty string to disable or filename to load from local file in static folder
+ privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy) Empty string to disable or filename to load from local file in static folder
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility) Empty string to disable or filename to load from local file in static folder
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie) Empty string to disable or filename to load from local file in static folder
impressum: '' # URL to the impressum of your application (e.g. https://example.com/impressum) Empty string to disable or filename to load from local file in static folder
@@ -72,6 +84,7 @@ system:
showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template html files
tessdataDir: /usr/share/tessdata # Path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
+ enableAnalytics: undefined # Set to 'true' to enable analytics, set to 'false' to disable analytics, for enterprise users this is set to true
ui:
appName: '' # Application's visible name
@@ -88,3 +101,4 @@ metrics:
# Automatically Generated Settings (Do Not Edit Directly)
AutomaticallyGenerated:
key: example
+ UUID: example
diff --git a/src/main/resources/static/3rdPartyLicenses.json b/src/main/resources/static/3rdPartyLicenses.json
index 4c8de54ad66..4c60ddd3ce6 100644
--- a/src/main/resources/static/3rdPartyLicenses.json
+++ b/src/main/resources/static/3rdPartyLicenses.json
@@ -21,6 +21,13 @@
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0"
},
+ {
+ "moduleName": "com.coveo:saml-client",
+ "moduleUrl": "https://github.com/coveo/saml-client",
+ "moduleVersion": "5.0.0",
+ "moduleLicense": "MIT",
+ "moduleLicenseUrl": "https://opensource.org/licenses/MIT"
+ },
{
"moduleName": "com.fasterxml.jackson.core:jackson-annotations",
"moduleUrl": "https://github.com/FasterXML/jackson",
@@ -63,6 +70,27 @@
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base",
+ "moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-base",
+ "moduleVersion": "2.17.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider",
+ "moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-json-provider",
+ "moduleVersion": "2.17.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.fasterxml.jackson.module:jackson-module-jaxb-annotations",
+ "moduleUrl": "https://github.com/FasterXML/jackson-modules-base",
+ "moduleVersion": "2.17.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names",
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names",
@@ -111,6 +139,46 @@
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "com.google.code.findbugs:jsr305",
+ "moduleUrl": "http://findbugs.sourceforge.net/",
+ "moduleVersion": "3.0.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.google.errorprone:error_prone_annotations",
+ "moduleVersion": "2.11.0",
+ "moduleLicense": "Apache 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.google.guava:failureaccess",
+ "moduleUrl": "https://github.com/google/guava/",
+ "moduleVersion": "1.0.1",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.google.guava:guava",
+ "moduleUrl": "https://github.com/google/guava/",
+ "moduleVersion": "31.1-jre",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.google.guava:listenablefuture",
+ "moduleVersion": "9999.0-empty-to-avoid-conflict-with-guava",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.google.j2objc:j2objc-annotations",
+ "moduleUrl": "https://github.com/google/j2objc/",
+ "moduleVersion": "1.3",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "com.google.zxing:core",
"moduleUrl": "https://github.com/zxing/zxing/core",
@@ -118,6 +186,25 @@
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "com.googlecode.owasp-java-html-sanitizer:java10-shim",
+ "moduleVersion": "20240325.1",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.googlecode.owasp-java-html-sanitizer:java8-shim",
+ "moduleVersion": "20240325.1",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer",
+ "moduleUrl": "https://owasp.org",
+ "moduleVersion": "20240325.1",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "com.h2database:h2",
"moduleUrl": "https://h2database.com",
@@ -167,6 +254,13 @@
"moduleLicense": "Apache 2",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "com.posthog.java:posthog",
+ "moduleUrl": "http://github.com/PostHog/posthog-java",
+ "moduleVersion": "1.1.1",
+ "moduleLicense": "MIT License",
+ "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php"
+ },
{
"moduleName": "com.sun.istack:istack-commons-runtime",
"moduleUrl": "https://www.eclipse.org",
@@ -176,64 +270,78 @@
},
{
"moduleName": "com.twelvemonkeys.common:common-image",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.common:common-io",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.common:common-lang",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-batik",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-bmp",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-core",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-jpeg",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-metadata",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-tiff",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-webp",
- "moduleVersion": "3.11.0",
+ "moduleVersion": "3.12.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
+ {
+ "moduleName": "com.unboundid.product.scim2:scim2-sdk-client",
+ "moduleUrl": "https://github.com/pingidentity/scim2",
+ "moduleVersion": "2.3.5",
+ "moduleLicense": "UnboundID SCIM2 SDK Free Use License",
+ "moduleLicenseUrl": "https://github.com/pingidentity/scim2"
+ },
+ {
+ "moduleName": "com.unboundid.product.scim2:scim2-sdk-common",
+ "moduleUrl": "https://github.com/pingidentity/scim2",
+ "moduleVersion": "2.3.5",
+ "moduleLicense": "UnboundID SCIM2 SDK Free Use License",
+ "moduleLicenseUrl": "https://github.com/pingidentity/scim2"
+ },
{
"moduleName": "com.zaxxer:HikariCP",
"moduleUrl": "https://github.com/brettwooldridge/HikariCP",
@@ -248,6 +356,13 @@
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "commons-codec:commons-codec",
+ "moduleUrl": "https://commons.apache.org/proper/commons-codec/",
+ "moduleVersion": "1.16.1",
+ "moduleLicense": "Apache-2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "commons-collections:commons-collections",
"moduleUrl": "http://commons.apache.org/collections/",
@@ -269,6 +384,12 @@
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "/LICENSE.txt"
},
+ {
+ "moduleName": "io.dropwizard.metrics:metrics-core",
+ "moduleVersion": "4.2.25",
+ "moduleLicense": "Apache License 2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.html"
+ },
{
"moduleName": "io.github.pixee:java-security-toolkit",
"moduleUrl": "https://github.com/pixee/java-security-toolkit",
@@ -286,7 +407,7 @@
{
"moduleName": "io.micrometer:micrometer-core",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
- "moduleVersion": "1.13.4",
+ "moduleVersion": "1.13.6",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -422,6 +543,20 @@
"moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
"moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
},
+ {
+ "moduleName": "javax.activation:javax.activation-api",
+ "moduleUrl": "http://www.oracle.com",
+ "moduleVersion": "1.2.0",
+ "moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0",
+ "moduleLicenseUrl": "https://opensource.org/licenses/CDDL-1.0"
+ },
+ {
+ "moduleName": "javax.xml.bind:jaxb-api",
+ "moduleUrl": "http://www.oracle.com/",
+ "moduleVersion": "2.3.1",
+ "moduleLicense": "GPL2 w/ CPE",
+ "moduleLicenseUrl": "https://oss.oracle.com/licenses/CDDL+GPL-1.1"
+ },
{
"moduleName": "net.bytebuddy:byte-buddy",
"moduleVersion": "1.14.19",
@@ -442,6 +577,12 @@
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "net.shibboleth.utilities:java-support",
+ "moduleVersion": "8.4.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "org.antlr:antlr4-runtime",
"moduleUrl": "https://www.antlr.org/",
@@ -470,6 +611,20 @@
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "org.apache.httpcomponents:httpclient",
+ "moduleUrl": "http://hc.apache.org/httpcomponents-client-ga",
+ "moduleVersion": "4.5.14",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.apache.httpcomponents:httpcore",
+ "moduleUrl": "http://hc.apache.org/httpcomponents-core-ga",
+ "moduleVersion": "4.4.16",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "org.apache.logging.log4j:log4j-api",
"moduleVersion": "2.23.1",
@@ -516,6 +671,13 @@
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "org.apache.santuario:xmlsec",
+ "moduleUrl": "https://www.apache.org/",
+ "moduleVersion": "2.3.4",
+ "moduleLicense": "Apache-2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "org.apache.tomcat.embed:tomcat-embed-el",
"moduleUrl": "https://tomcat.apache.org/",
@@ -523,16 +685,23 @@
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
+ {
+ "moduleName": "org.apache.velocity:velocity-engine-core",
+ "moduleUrl": "https://www.apache.org/",
+ "moduleVersion": "2.3",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "org.apache.xmlgraphics:batik-all",
- "moduleVersion": "1.17",
+ "moduleVersion": "1.18",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.apache.xmlgraphics:xmlgraphics-commons",
"moduleUrl": "http://xmlgraphics.apache.org/commons/",
- "moduleVersion": "2.9",
+ "moduleVersion": "2.10",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -571,18 +740,32 @@
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
+ {
+ "moduleName": "org.checkerframework:checker-qual",
+ "moduleUrl": "https://checkerframework.org",
+ "moduleVersion": "3.12.0",
+ "moduleLicense": "The MIT License",
+ "moduleLicenseUrl": "http://opensource.org/licenses/MIT"
+ },
{
"moduleName": "org.commonmark:commonmark",
- "moduleVersion": "0.23.0",
- "moduleLicense": "BSD 2-Clause License",
+ "moduleVersion": "0.24.0",
+ "moduleLicense": "BSD-2-Clause",
"moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "org.commonmark:commonmark-ext-gfm-tables",
- "moduleVersion": "0.23.0",
- "moduleLicense": "BSD 2-Clause License",
+ "moduleVersion": "0.24.0",
+ "moduleLicense": "BSD-2-Clause",
"moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause"
},
+ {
+ "moduleName": "org.cryptacular:cryptacular",
+ "moduleUrl": "http://www.cryptacular.org",
+ "moduleVersion": "1.2.5",
+ "moduleLicense": "GNU Lesser General Public License",
+ "moduleLicenseUrl": "http://www.gnu.org/licenses/lgpl-3.0.txt"
+ },
{
"moduleName": "org.eclipse.angus:angus-activation",
"moduleUrl": "https://www.eclipse.org",
@@ -828,6 +1011,78 @@
"moduleLicense": "Public Domain, per Creative Commons CC0",
"moduleLicenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/"
},
+ {
+ "moduleName": "org.opensaml:opensaml-core",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-messaging-api",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-profile-api",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-saml-api",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-saml-impl",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-security-api",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-security-impl",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-soap-api",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-soap-impl",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-storage-api",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-xmlsec-api",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ {
+ "moduleName": "org.opensaml:opensaml-xmlsec-impl",
+ "moduleVersion": "4.3.2",
+ "moduleLicense": "The Apache Software License, Version 2.0",
+ "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
{
"moduleName": "org.ow2.asm:asm",
"moduleUrl": "http://asm.ow2.org",
@@ -1056,6 +1311,13 @@
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
+ {
+ "moduleName": "org.springframework.security:spring-security-saml2-service-provider",
+ "moduleUrl": "https://spring.io/projects/spring-security",
+ "moduleVersion": "6.3.3",
+ "moduleLicense": "Apache License, Version 2.0",
+ "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+ },
{
"moduleName": "org.springframework.security:spring-security-web",
"moduleUrl": "https://spring.io/projects/spring-security",
@@ -1143,7 +1405,7 @@
{
"moduleName": "org.springframework:spring-webmvc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
- "moduleVersion": "6.1.13",
+ "moduleVersion": "6.1.14",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
diff --git a/src/main/resources/static/css/bootstrap.min.css b/src/main/resources/static/css/bootstrap.min.css
index 92e022d3d45..5c4d3c11ac6 100644
--- a/src/main/resources/static/css/bootstrap.min.css
+++ b/src/main/resources/static/css/bootstrap.min.css
@@ -3,5 +3,5 @@
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0))}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-font-sans-serif);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x) * -.5);margin-left:calc(var(--bs-gutter-x) * -.5)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + (.5rem + 2px));padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + (1rem + 2px));padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + (.75rem + 2px))}textarea.form-control-sm{min-height:calc(1.5em + (.5rem + 2px))}textarea.form-control-lg{min-height:calc(1.5em + (1rem + 2px))}.form-control-color{max-width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast:not(.showing):not(.show){opacity:0}.toast.hide{display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1060;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:600px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1050;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{color:#0d6efd!important}.text-secondary{color:#6c757d!important}.text-success{color:#198754!important}.text-info{color:#0dcaf0!important}.text-warning{color:#ffc107!important}.text-danger{color:#dc3545!important}.text-light{color:#f8f9fa!important}.text-dark{color:#212529!important}.text-white{color:#fff!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-reset{color:inherit!important}.bg-primary{background-color:#0d6efd!important}.bg-secondary{background-color:#6c757d!important}.bg-success{background-color:#198754!important}.bg-info{background-color:#0dcaf0!important}.bg-warning{background-color:#ffc107!important}.bg-danger{background-color:#dc3545!important}.bg-light{background-color:#f8f9fa!important}.bg-dark{background-color:#212529!important}.bg-body{background-color:#fff!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}
+ */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0))}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-font-sans-serif);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1340px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x) * -.5);margin-left:calc(var(--bs-gutter-x) * -.5)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + (.5rem + 2px));padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + (1rem + 2px));padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + (.75rem + 2px))}textarea.form-control-sm{min-height:calc(1.5em + (.5rem + 2px))}textarea.form-control-lg{min-height:calc(1.5em + (1rem + 2px))}.form-control-color{max-width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast:not(.showing):not(.show){opacity:0}.toast.hide{display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1060;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:600px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1050;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{color:#0d6efd!important}.text-secondary{color:#6c757d!important}.text-success{color:#198754!important}.text-info{color:#0dcaf0!important}.text-warning{color:#ffc107!important}.text-danger{color:#dc3545!important}.text-light{color:#f8f9fa!important}.text-dark{color:#212529!important}.text-white{color:#fff!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-reset{color:inherit!important}.bg-primary{background-color:#0d6efd!important}.bg-secondary{background-color:#6c757d!important}.bg-success{background-color:#198754!important}.bg-info{background-color:#0dcaf0!important}.bg-warning{background-color:#ffc107!important}.bg-danger{background-color:#dc3545!important}.bg-light{background-color:#f8f9fa!important}.bg-dark{background-color:#212529!important}.bg-body{background-color:#fff!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}
/*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/src/main/resources/static/css/multi-tool.css b/src/main/resources/static/css/multi-tool.css
index 295659086ce..9687470a899 100644
--- a/src/main/resources/static/css/multi-tool.css
+++ b/src/main/resources/static/css/multi-tool.css
@@ -77,7 +77,7 @@ label {
flex-direction: column;
padding: 1rem;
border-radius: 25px;
- overflow-y: hidden;
+ overflow-y: auto;
overflow-x: auto;
min-height: 275px;
margin: 0 0 30px 0;
diff --git a/src/main/resources/static/css/navbar.css b/src/main/resources/static/css/navbar.css
index 7db824628c3..659940b9133 100644
--- a/src/main/resources/static/css/navbar.css
+++ b/src/main/resources/static/css/navbar.css
@@ -89,6 +89,14 @@
width: 80%;
}
+.close-icon {
+ color: var(--md-sys-color-secondary);
+}
+
+.close-icon:hover {
+ transform: scale(1.15);
+}
+
span.icon-text::after {
content: attr(data-text);
content: attr(data-text) / "";
@@ -320,3 +328,26 @@ span.icon-text::after {
display: none;
}
}
+
+ .go-pro-link {
+ position: relative;
+ padding: 0.5rem 1rem;
+ transition: all 0.3s ease;
+ }
+
+.go-pro-badge {
+ display: inline-block;
+ padding: 0.25rem 0.5rem;
+ font-size: 0.75rem;
+ font-weight: bold;
+ color: #ffffff;
+ background-color: #007bff;
+ border-radius: 0.25rem;
+ text-transform: uppercase;
+ transition: all 0.3s ease;
+}
+
+.go-pro-link:hover .go-pro-badge {
+ background-color: #0056b3;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
\ No newline at end of file
diff --git a/src/main/resources/static/css/rotate-pdf.css b/src/main/resources/static/css/rotate-pdf.css
index 57a30024d82..19024e2071f 100644
--- a/src/main/resources/static/css/rotate-pdf.css
+++ b/src/main/resources/static/css/rotate-pdf.css
@@ -27,3 +27,16 @@
display: flex;
justify-content: space-around;
}
+
+#pdf-preview-large {
+ margin: 0 auto;
+ display: block;
+ max-width: calc(100% - 30px);
+ max-height: calc(100% - 30px);
+ box-shadow: 0 0 4px rgba(100, 100, 100, 0.25);
+ transition: rotate 0.3s;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ translate: -50% -50%;
+}
diff --git a/src/main/resources/static/css/sign.css b/src/main/resources/static/css/sign.css
index c6ae3374b73..26bb1677485 100644
--- a/src/main/resources/static/css/sign.css
+++ b/src/main/resources/static/css/sign.css
@@ -1,7 +1,9 @@
select#font-select,
select#font-select option {
- height: 60px; /* Adjust as needed */
- font-size: 30px; /* Adjust as needed */
+ height: 60px;
+ /* Adjust as needed */
+ font-size: 30px;
+ /* Adjust as needed */
}
.drawing-pad-container {
@@ -13,10 +15,12 @@ select#font-select option {
width: 100%;
height: 300px;
}
+
#box-drag-container {
position: relative;
margin: 20px 0;
}
+
.draggable-buttons-box {
position: absolute;
top: 0;
@@ -24,16 +28,37 @@ select#font-select option {
width: 100%;
display: flex;
gap: 5px;
+ z-index: 5;
}
-.draggable-buttons-box > button {
- z-index: 10;
+
+.draggable-buttons-box>button {
+ z-index: 4;
background-color: rgba(13, 110, 253, 0.1);
}
+
.draggable-canvas {
- border: 1px solid red;
+ border: 2px solid #3498db;
position: absolute;
touch-action: none;
user-select: none;
top: 0px;
left: 0;
+ z-index: 100;
+ cursor: grab;
+ transition: transform 0.1s ease-out;
+ background-color: rgba(52, 152, 219, 0.1);
+ /* Light blue background */
+}
+
+.draggable-canvas:active {
+ cursor: grabbing;
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
+ /* Shadow on active drag */
+}
+
+.draggable-canvas:hover {
+ border: 2px solid #2980b9;
+ /* Darker border on hover */
+ background-color: rgba(52, 152, 219, 0.2);
+ /* Darken background on hover */
}
diff --git a/src/main/resources/static/js/downloader.js b/src/main/resources/static/js/downloader.js
index 46e6a94833b..bbe16ba5266 100644
--- a/src/main/resources/static/js/downloader.js
+++ b/src/main/resources/static/js/downloader.js
@@ -5,6 +5,22 @@ function showErrorBanner(message, stackTrace) {
document.querySelector("#errorContainer p").textContent = message;
document.querySelector("#traceContent").textContent = stackTrace;
}
+
+function showSessionExpiredPrompt() {
+ const errorContainer = document.getElementById("errorContainer");
+ errorContainer.style.display = "block";
+ document.querySelector("#errorContainer .alert-heading").textContent = sessionExpired;
+ document.querySelector("#errorContainer p").textContent = sessionExpired;
+ document.querySelector("#traceContent").textContent = "";
+
+ // Optional: Add a refresh button
+ const refreshButton = document.createElement("button");
+ refreshButton.textContent = "Refresh Page";
+ refreshButton.className = "btn btn-primary mt-3";
+ refreshButton.onclick = () => location.reload();
+ errorContainer.appendChild(refreshButton);
+}
+
let firstErrorOccurred = false;
$(document).ready(function () {
@@ -79,6 +95,11 @@ async function handleSingleDownload(url, formData, isMulti = false, isZip = fals
const contentType = response.headers.get("content-type");
if (!response.ok) {
+ if (response.status === 401) {
+ // Handle 401 Unauthorized error
+ showSessionExpiredPrompt();
+ return;
+ }
if (contentType && contentType.includes("application/json")) {
console.error("Throwing error banner, response was not okay");
return handleJsonResponse(response);
@@ -97,7 +118,7 @@ async function handleSingleDownload(url, formData, isMulti = false, isZip = fals
}
} catch (error) {
console.error("Error in handleSingleDownload:", error);
- throw error; // Re-throw the error if you want it to be handled higher up.
+ throw error;
}
}
diff --git a/src/main/resources/static/js/draggable.js b/src/main/resources/static/js/draggable.js
new file mode 100644
index 00000000000..cba6f3c0268
--- /dev/null
+++ b/src/main/resources/static/js/draggable.js
@@ -0,0 +1,36 @@
+const draggableElement = document.querySelector('.draggable-canvas');
+
+// Variables to store the current position of the draggable element
+let offsetX, offsetY, isDragging = false;
+
+draggableElement.addEventListener('mousedown', (e) => {
+ // Get the offset when the mouse is clicked inside the element
+ offsetX = e.clientX - draggableElement.getBoundingClientRect().left;
+ offsetY = e.clientY - draggableElement.getBoundingClientRect().top;
+
+ // Set isDragging to true
+ isDragging = true;
+
+ // Add event listeners for mouse movement and release
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+});
+
+function onMouseMove(e) {
+ if (isDragging) {
+ // Calculate the new position of the element
+ const left = e.clientX - offsetX;
+ const top = e.clientY - offsetY;
+
+ // Move the element by setting its style
+ draggableElement.style.left = `${left}px`;
+ draggableElement.style.top = `${top}px`;
+ }
+}
+
+function onMouseUp() {
+ // Stop dragging and remove event listeners
+ isDragging = false;
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+}
diff --git a/src/main/resources/static/js/favourites.js b/src/main/resources/static/js/favourites.js
index dbecd013f81..16e219f77c8 100644
--- a/src/main/resources/static/js/favourites.js
+++ b/src/main/resources/static/js/favourites.js
@@ -1,45 +1,73 @@
function updateFavoritesDropdown() {
var dropdown = document.querySelector("#favoritesDropdown");
- // Check if dropdown exists
if (!dropdown) {
console.error('Dropdown element with ID "favoritesDropdown" not found!');
- return; // Exit the function
+ return;
}
- dropdown.innerHTML = ""; // Clear the current favorites
+ dropdown.innerHTML = "";
var hasFavorites = false;
+ var addedFeatures = new Set();
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
- if (localStorage.getItem(key) === "favorite") {
- // Find the corresponding navbar entry
+ var value = localStorage.getItem(key);
+
+ if (value === "favorite") {
var navbarEntry = document.querySelector(`a[href='${key}']`);
if (navbarEntry) {
- // Create a new dropdown entry
- var dropdownItem = document.createElement("a");
- dropdownItem.className = "dropdown-item";
- dropdownItem.href = navbarEntry.href;
- dropdownItem.innerHTML = navbarEntry.innerHTML;
- dropdown.appendChild(dropdownItem);
- hasFavorites = true;
+ var featureName = navbarEntry.textContent.trim();
+
+ if (!addedFeatures.has(featureName)) {
+ var dropdownItem = document.createElement("div");
+ dropdownItem.className = "dropdown-item d-flex justify-content-between align-items-center";
+
+ // Create a wrapper for the original content
+ var contentWrapper = document.createElement("div");
+ contentWrapper.className = "d-flex align-items-center flex-grow-1";
+ contentWrapper.style.textDecoration = "none";
+ contentWrapper.style.color = "inherit";
+
+ // Clone the original content
+ var originalContent = navbarEntry.querySelector('div').cloneNode(true);
+ contentWrapper.appendChild(originalContent);
+
+ // Create the remove button
+ var removeButton = document.createElement("button");
+ removeButton.className = "btn btn-sm btn-link p-0 ml-2";
+ removeButton.innerHTML = 'close ';
+ removeButton.onclick = function(itemKey, event) {
+ event.preventDefault();
+ event.stopPropagation();
+ localStorage.removeItem(itemKey);
+ updateFavoritesSection();
+ updateFavoritesDropdown();
+ filterCards();
+ }.bind(null, key);
+
+ // Add click event to the content wrapper
+ contentWrapper.onclick = function(itemHref, event) {
+ event.preventDefault();
+ window.location.href = itemHref;
+ }.bind(null, navbarEntry.href);
+
+ dropdownItem.appendChild(contentWrapper);
+ dropdownItem.appendChild(removeButton);
+ dropdown.appendChild(dropdownItem);
+ hasFavorites = true;
+ addedFeatures.add(featureName);
+ }
} else {
console.warn(`Navbar entry not found for key: ${key}`);
}
}
}
- // Show or hide the default item based on whether there are any favorites
if (!hasFavorites) {
var defaultItem = document.createElement("a");
defaultItem.className = "dropdown-item";
- defaultItem.textContent = noFavourites;
+ defaultItem.textContent = noFavourites || "No favorites added";
dropdown.appendChild(defaultItem);
}
}
-
-// Ensure that the DOM content has been fully loaded before calling the function
-document.addEventListener("DOMContentLoaded", function () {
- console.log("DOMContentLoaded event fired");
- updateFavoritesDropdown();
-});
diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js
index d4dfd7ea42f..efc5314e226 100644
--- a/src/main/resources/static/js/homecard.js
+++ b/src/main/resources/static/js/homecard.js
@@ -25,29 +25,38 @@ function filterCards() {
function updateFavoritesSection() {
const favoritesContainer = document.getElementById("groupFavorites").querySelector(".feature-group-container");
favoritesContainer.style.maxHeight = "none";
- favoritesContainer.innerHTML = "";
- const cards = Array.from(document.querySelectorAll(".feature-card"));
+ favoritesContainer.innerHTML = ""; // Clear the container first
+ const cards = Array.from(document.querySelectorAll(".feature-card:not(.duplicate)"));
+ const addedCardIds = new Set(); // To keep track of added card IDs
let favoritesAmount = 0;
+
cards.forEach(card => {
- if (localStorage.getItem(card.id) === "favorite") {
+ if (localStorage.getItem(card.id) === "favorite" && !addedCardIds.has(card.id)) {
const duplicate = card.cloneNode(true);
+ duplicate.classList.add("duplicate");
favoritesContainer.appendChild(duplicate);
+ addedCardIds.add(card.id); // Mark this card as added
favoritesAmount++;
}
});
+
if (favoritesAmount === 0) {
document.getElementById("groupFavorites").style.display = "none";
} else {
document.getElementById("groupFavorites").style.display = "flex";
- };
+ }
reorderCards(favoritesContainer);
favoritesContainer.style.maxHeight = favoritesContainer.scrollHeight + "px";
-};
+}
function toggleFavorite(element) {
var span = element.querySelector("span.material-symbols-rounded");
var card = element.closest(".feature-card");
var cardId = card.id;
+
+ // Prevent the event from bubbling up to parent elements
+ event.stopPropagation();
+
if (span.classList.contains("no-fill")) {
span.classList.remove("no-fill");
span.classList.add("fill");
@@ -59,7 +68,31 @@ function toggleFavorite(element) {
card.classList.remove("favorite");
localStorage.removeItem(cardId);
}
- reorderCards(card.parentNode);
+
+ // Use setTimeout to ensure this runs after the current call stack is clear
+ setTimeout(() => {
+ reorderCards(card.parentNode);
+ updateFavoritesSection();
+ updateFavoritesDropdown();
+ filterCards();
+ }, 0);
+}
+
+function syncFavorites() {
+ const cards = Array.from(document.querySelectorAll(".feature-card"));
+ cards.forEach(card => {
+ const isFavorite = localStorage.getItem(card.id) === "favorite";
+ const starIcon = card.querySelector(".favorite-icon span.material-symbols-rounded");
+ if (isFavorite) {
+ starIcon.classList.remove("no-fill");
+ starIcon.classList.add("fill");
+ card.classList.add("favorite");
+ } else {
+ starIcon.classList.remove("fill");
+ starIcon.classList.add("no-fill");
+ card.classList.remove("favorite");
+ }
+ });
updateFavoritesSection();
updateFavoritesDropdown();
filterCards();
@@ -181,7 +214,10 @@ function expandCollapseAll(expandAll) {
})
}
-window.onload = initializeCards;
+window.onload = function() {
+ initializeCards();
+ syncFavorites(); // Ensure everything is in sync on page load
+};
document.addEventListener("DOMContentLoaded", function () {
const materialIcons = new FontFaceObserver('Material Symbols Rounded');
@@ -223,7 +259,5 @@ document.addEventListener("DOMContentLoaded", function () {
})
}, 500);
-
-
showFavoritesOnly();
-});
+});
\ No newline at end of file
diff --git a/src/main/resources/static/js/multitool/PdfActionsManager.js b/src/main/resources/static/js/multitool/PdfActionsManager.js
index 3701266e2d4..033b399191e 100644
--- a/src/main/resources/static/js/multitool/PdfActionsManager.js
+++ b/src/main/resources/static/js/multitool/PdfActionsManager.js
@@ -192,6 +192,7 @@ class PdfActionsManager {
return div;
}
+
}
export default PdfActionsManager;
diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js
index 55635c69e8c..0cc1a110738 100644
--- a/src/main/resources/static/js/multitool/PdfContainer.js
+++ b/src/main/resources/static/js/multitool/PdfContainer.js
@@ -117,6 +117,7 @@ class PdfContainer {
const newAngle = lastAngle + deg;
element.style.rotate = newAngle + "deg";
+
}
async addPdfFile(file, nextSiblingElement) {
@@ -326,6 +327,9 @@ class PdfContainer {
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
}
}
+ pdfDoc.setCreator(stirlingPDFLabel);
+ pdfDoc.setProducer(stirlingPDFLabel);
+
const pdfBytes = await pdfDoc.save();
const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
diff --git a/src/main/resources/static/js/multitool/horizontalScroll.js b/src/main/resources/static/js/multitool/horizontalScroll.js
index 2d20fd7b269..28ec42f51bd 100644
--- a/src/main/resources/static/js/multitool/horizontalScroll.js
+++ b/src/main/resources/static/js/multitool/horizontalScroll.js
@@ -1,27 +1,32 @@
const scrollDivHorizontally = (id) => {
- var scrollDelta = 0; // variable to store the accumulated scroll delta
+ var scrollDeltaX = 0; // variable to store the accumulated horizontal scroll delta
+ var scrollDeltaY = 0; // variable to store the accumulated vertical scroll delta
var isScrolling = false; // variable to track if scroll is already in progress
- const divToScrollHorizontally = document.getElementById(id);
+ const divToScroll = document.getElementById(id);
+
function scrollLoop() {
- // Scroll the div horizontally by a fraction of the accumulated scroll delta
- divToScrollHorizontally.scrollLeft += scrollDelta * 0.1;
+ // Scroll the div horizontally and vertically by a fraction of the accumulated scroll delta
+ divToScroll.scrollLeft += scrollDeltaX * 0.1;
+ divToScroll.scrollTop += scrollDeltaY * 0.1;
// Reduce the accumulated scroll delta by a fraction
- scrollDelta *= 0.9;
+ scrollDeltaX *= 0.9;
+ scrollDeltaY *= 0.9;
// If scroll delta is still significant, continue the scroll loop
- if (Math.abs(scrollDelta) > 0.1) {
+ if (Math.abs(scrollDeltaX) > 0.1 || Math.abs(scrollDeltaY) > 0.1) {
requestAnimationFrame(scrollLoop);
} else {
isScrolling = false; // Reset scroll in progress flag
}
}
- divToScrollHorizontally.addEventListener("wheel", function (e) {
+ divToScroll.addEventListener("wheel", function (e) {
e.preventDefault(); // prevent default mousewheel behavior
- // Accumulate the horizontal scroll delta
- scrollDelta -= e.deltaX || e.wheelDeltaX || -e.deltaY || -e.wheelDeltaY;
+ // Accumulate the horizontal and vertical scroll delta
+ scrollDeltaX -= e.deltaX || e.wheelDeltaX || -e.deltaY || -e.wheelDeltaY;
+ scrollDeltaY -= e.deltaY || e.wheelDeltaY || -e.deltaX || -e.wheelDeltaX;
// If scroll is not already in progress, start the scroll loop
if (!isScrolling) {
@@ -31,4 +36,4 @@ const scrollDivHorizontally = (id) => {
});
};
-export default scrollDivHorizontally;
+export default scrollDivHorizontally;
\ No newline at end of file
diff --git a/src/main/resources/templates/account.html b/src/main/resources/templates/account.html
index a0e54397824..c9d087236b2 100644
--- a/src/main/resources/templates/account.html
+++ b/src/main/resources/templates/account.html
@@ -278,7 +278,7 @@ Settings Comparison:
let allKeys = new Set([...Object.keys(accountSettings), ...Object.keys(localStorage)]);
allKeys.forEach(key => {
- if(key === 'debug' || key === '0' || key === '1') return; // Ignoring specific keys
+ if(key === 'debug' || key === '0' || key === '1' || key.includes('pdfjs') || key.includes('posthog') || key.includes('pageViews')) return; // Ignoring specific keys
const accountValue = accountSettings[key] || '-';
const browserValue = localStorage.getItem(key) || '-';
@@ -299,7 +299,7 @@ Settings Comparison:
// Then, set the account settings to local storage
for (let key in accountSettings) {
- if(key !== 'debug' && key !== '0' && key !== '1') { // Only sync non-ignored keys
+ if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) { // Only sync non-ignored keys
localStorage.setItem(key, accountSettings[key]);
}
}
@@ -316,7 +316,7 @@ Settings Comparison:
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
- if(key !== 'debug' && key !== '0' && key !== '1') { // Only send non-ignored keys
+ if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) { // Only send non-ignored keys
let hiddenField = document.createElement("input");
hiddenField.type = "hidden";
hiddenField.name = key;
diff --git a/src/main/resources/templates/addUsers.html b/src/main/resources/templates/addUsers.html
index a91f0e46421..e17e165adbd 100644
--- a/src/main/resources/templates/addUsers.html
+++ b/src/main/resources/templates/addUsers.html
@@ -101,6 +101,8 @@
+
+
-
+
-
+
star
diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html
index 700c68fd23d..293a8549b3a 100644
--- a/src/main/resources/templates/fragments/common.html
+++ b/src/main/resources/templates/fragments/common.html
@@ -69,6 +69,49 @@
+
+
+
+
@@ -148,6 +191,7 @@
const multipleInputsForSingleRequest = /*[[${multipleInputsForSingleRequest}]]*/ false;
const disableMultipleFiles = /*[[${disableMultipleFiles}]]*/ false;
const remoteCall = /*[[${remoteCall}]]*/ true;
+ const sessionExpired = /*[[#{session.expired}]]*/ '';
diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html
index 40f64419a3f..6cf37fd4a18 100644
--- a/src/main/resources/templates/fragments/footer.html
+++ b/src/main/resources/templates/fragments/footer.html
@@ -4,7 +4,7 @@
-
+
diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html
index fe0c8842d30..24a2ca5714a 100644
--- a/src/main/resources/templates/fragments/navbar.html
+++ b/src/main/resources/templates/fragments/navbar.html
@@ -229,6 +229,9 @@
+
+
@@ -357,6 +360,12 @@
+
+
+
+
+
+
diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html
index 7f448f7a67f..279b0f6c7ca 100644
--- a/src/main/resources/templates/home.html
+++ b/src/main/resources/templates/home.html
@@ -68,11 +68,39 @@
+
+
+
+
+
+
+
+
@@ -199,6 +227,9 @@
+
+
@@ -215,9 +246,7 @@
-
-
+
@@ -227,9 +256,6 @@
-
-
@@ -263,15 +289,15 @@
-
-
+
+
@@ -293,6 +319,9 @@
+
+
@@ -303,8 +332,7 @@
-
+
-
Stirling-PDF has no tracking so we want to hear from our users to improve
- Stirling-PDF!
+
Stirling-PDF has changed since the last survey! To find out more please check our blog post here: Stirling PDF
+
+
With these changes we are getting paid business support and funding
Please consider taking our survey!
-
Survey popup will be disabled in following updates but available at foot of
- page)
-
Take Survey
+
Survey popup will be disabled in following updates but available at foot of page)
+
Take Survey
-