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 + + + + + + + +
+
+ +

+
+
+
+
+ upload + +
+
+
+ +
+ + +
+ + +
+
+
+
+
+ +
+ + + ``` + +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 + + ``` + + +## 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

+ + +``` + + + +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 @@ +

+ - @@ -303,8 +332,7 @@
-