Merge branch 'main' into session_2025_03_22

This commit is contained in:
Ludy 2025-05-14 22:18:34 +02:00 committed by GitHub
commit d923e65b94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 1846 additions and 252 deletions

View File

@ -24,4 +24,4 @@ jobs:
- name: "Checkout Repository"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: "Dependency Review"
uses: actions/dependency-review-action@ce3cf9537a52e8119d91fd484ab5b8a807627bf8 # v4.6.0
uses: actions/dependency-review-action@38ecb5b593bf0eb19e335c03f97670f792489a8b # v4.7.0

View File

@ -541,7 +541,7 @@ This would generate n entries of tr for each person in exampleData
```html
<li class="nav-item">
<a class="nav-link" th:href="@{/new-feature}">New Feature</a>
<a class="nav-link" th:href="@{'/new-feature'}">New Feature</a>
</li>
```

View File

@ -112,7 +112,7 @@ Visit our comprehensive documentation at [docs.stirlingpdf.com](https://docs.sti
## Supported Languages
Stirling-PDF currently supports 39 languages!
Stirling-PDF currently supports 40 languages!
| Language | Progress |
| -------------------------------------------- | -------------------------------------- |
@ -127,7 +127,7 @@ Stirling-PDF currently supports 39 languages!
| Dutch (Nederlands) (nl_NL) | ![79%](https://geps.dev/progress/79) |
| 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) | ![91%](https://geps.dev/progress/91) |
| French (Français) (fr_FR) | ![92%](https://geps.dev/progress/92) |
| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) |
| Greek (Ελληνικά) (el_GR) | ![91%](https://geps.dev/progress/91) |
| Hindi (हिंदी) (hi_IN) | ![91%](https://geps.dev/progress/91) |
@ -156,12 +156,12 @@ Stirling-PDF currently supports 39 languages!
| Turkish (Türkçe) (tr_TR) | ![97%](https://geps.dev/progress/97) |
| Ukrainian (Українська) (uk_UA) | ![96%](https://geps.dev/progress/96) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![73%](https://geps.dev/progress/73) |
| Malayalam (മലയാളം) (ml_ML) | ![99%](https://geps.dev/progress/99) |
## Stirling PDF Enterprise
Stirling PDF offers an Enterprise edition of its software. This is the same great software but with added features, support and comforts.
Check out our [Enterprise docs](https://docs.stirlingpdf.com/Enterprise%20Edition)
Check out our [Enterprise docs](https://docs.stirlingpdf.com/Pro)
## 🤝 Looking to contribute?

View File

@ -29,7 +29,7 @@ ext {
}
group = "stirling.software"
version = "0.46.0"
version = "0.46.1"
java {
// 17 is lowest but we support and recommend 21
@ -481,7 +481,7 @@ dependencies {
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
// Batik
implementation "org.apache.xmlgraphics:batik-all:1.18"
implementation "org.apache.xmlgraphics:batik-all:1.19"
// TwelveMonkeys
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
@ -529,7 +529,7 @@ 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.14.6"
implementation "io.micrometer:micrometer-core:1.14.7"
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark:0.24.0"

View File

@ -34,6 +34,11 @@ public class EEAppConfig {
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
}
@Bean(name = "license")
public String licenseType() {
return licenseKeyChecker.getPremiumLicenseEnabledResult().name();
}
@Bean(name = "runningEE")
public boolean runningEnterprise() {
return licenseKeyChecker.getPremiumLicenseEnabledResult() == License.ENTERPRISE;

View File

@ -5,6 +5,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.function.Predicate;
@ -15,6 +16,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
@ -33,6 +35,8 @@ public class AppConfig {
private final ApplicationProperties applicationProperties;
private final Environment env;
@Bean
@ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
@ -193,4 +197,37 @@ public class AppConfig {
public String uuid() {
return applicationProperties.getAutomaticallyGenerated().getUUID();
}
@Bean(name = "disablePixel")
public boolean disablePixel() {
return Boolean.getBoolean(env.getProperty("DISABLE_PIXEL"));
}
@Bean(name = "machineType")
public String determineMachineType() {
try {
boolean isDocker = runningInDocker();
boolean isKubernetes = System.getenv("KUBERNETES_SERVICE_HOST") != null;
boolean isBrowserOpen = "true".equalsIgnoreCase(env.getProperty("BROWSER_OPEN"));
if (isKubernetes) {
return "Kubernetes";
} else if (isDocker) {
return "Docker";
} else if (isBrowserOpen) {
String os = System.getProperty("os.name").toLowerCase(Locale.ROOT);
if (os.contains("win")) {
return "Client-windows";
} else if (os.contains("mac")) {
return "Client-mac";
} else {
return "Client-unix";
}
} else {
return "Server-jar";
}
} catch (Exception e) {
return "Unknown";
}
}
}

View File

@ -73,7 +73,7 @@ public class InitialSetup {
// Initialize Terms and Conditions
String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
if (StringUtils.isEmpty(termsUrl)) {
String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions";
String defaultTermsUrl = "https://www.stirlingpdf.com/terms";
GeneralUtils.saveKeyToSettings("legal.termsAndConditions", defaultTermsUrl);
applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl);
}

View File

@ -15,7 +15,7 @@ public class MetricsConfig {
return new MeterFilter() {
@Override
public MeterFilterReply accept(Meter.Id id) {
if (id.getName().equals("http.requests")) {
if ("http.requests".equals(id.getName())) {
return MeterFilterReply.NEUTRAL;
}
return MeterFilterReply.DENY;

View File

@ -5,7 +5,9 @@ import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
@ -31,14 +33,25 @@ public class OpenApiConfig {
// default version if all else fails
version = "1.0.0";
}
Info info =
new Info()
.title(DEFAULT_TITLE)
.version(version)
.license(
new License()
.name("MIT")
.url(
"https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/refs/heads/main/LICENSE")
.identifier("MIT"))
.termsOfService("https://www.stirlingpdf.com/terms")
.contact(
new Contact()
.name("Stirling Software")
.url("https://www.stirlingpdf.com")
.email("contact@stirlingpdf.com"))
.description(DEFAULT_DESCRIPTION);
if (!applicationProperties.getSecurity().getEnableLogin()) {
return new OpenAPI()
.components(new Components())
.info(
new Info()
.title(DEFAULT_TITLE)
.version(version)
.description(DEFAULT_DESCRIPTION));
return new OpenAPI().components(new Components()).info(info);
} else {
SecurityScheme apiKeyScheme =
new SecurityScheme()
@ -47,11 +60,7 @@ public class OpenApiConfig {
.name("X-API-KEY");
return new OpenAPI()
.components(new Components().addSecuritySchemes("apiKey", apiKeyScheme))
.info(
new Info()
.title(DEFAULT_TITLE)
.version(version)
.description(DEFAULT_DESCRIPTION))
.info(info)
.addSecurityItem(new SecurityRequirement().addList("apiKey"));
}
}

View File

@ -180,14 +180,15 @@ public class AnalysisController {
// Get permissions
Map<String, Boolean> permissions = new HashMap<>();
permissions.put("canPrint", document.getCurrentAccessPermission().canPrint());
permissions.put("canModify", document.getCurrentAccessPermission().canModify());
permissions.put("preventPrinting", !document.getCurrentAccessPermission().canPrint());
permissions.put(
"canExtractContent",
document.getCurrentAccessPermission().canExtractContent());
"preventModify", !document.getCurrentAccessPermission().canModify());
permissions.put(
"canModifyAnnotations",
document.getCurrentAccessPermission().canModifyAnnotations());
"preventExtractContent",
!document.getCurrentAccessPermission().canExtractContent());
permissions.put(
"preventModifyAnnotations",
!document.getCurrentAccessPermission().canModifyAnnotations());
securityInfo.put("permissions", permissions);
} else {

View File

@ -626,25 +626,32 @@ public class CompressController {
// Scale factors for different optimization levels
private double getScaleFactorForLevel(int optimizeLevel) {
return switch (optimizeLevel) {
case 4 -> 0.9; // 90% - lite compression
case 5 -> 0.8; // 80% - lite compression
case 6 -> 0.7; // 70% - lite compression
case 7 -> 0.6; // 60% - intense compression
case 8 -> 0.5; // 50% - intense compression
case 9, 10 -> 0.4; // 40% - intense compression
default -> 1.0; // No scaling for levels 1-3
};
return switch (optimizeLevel) {
case 3 -> 0.85;
case 4 -> 0.75;
case 5 -> 0.65;
case 6 -> 0.55;
case 7 -> 0.45;
case 8 -> 0.35;
case 9 -> 0.25;
case 10 -> 0.15;
default -> 1.0;
};
}
// JPEG quality for different optimization levels
private float getJpegQualityForLevel(int optimizeLevel) {
return switch (optimizeLevel) {
case 7 -> 0.8f; // 80% quality
case 8 -> 0.6f; // 60% quality
case 9, 10 -> 0.4f; // 40% quality
default -> 0.7f; // 70% quality for levels 1-6
};
return switch (optimizeLevel) {
case 3 -> 0.85f;
case 4 -> 0.80f;
case 5 -> 0.75f;
case 6 -> 0.70f;
case 7 -> 0.60f;
case 8 -> 0.50f;
case 9 -> 0.35f;
case 10 -> 0.2f;
default -> 0.7f;
};
}
@PostMapping(consumes = "multipart/form-data", value = "/compress-pdf")
@ -698,7 +705,7 @@ public class CompressController {
while (!sizeMet && optimizeLevel <= 9) {
// Apply image compression for levels 4-9
if ((optimizeLevel >= 4 || Boolean.TRUE.equals(convertToGrayscale))
if ((optimizeLevel >= 3 || Boolean.TRUE.equals(convertToGrayscale))
&& !imageCompressionApplied) {
double scaleFactor = getScaleFactorForLevel(optimizeLevel);
float jpegQuality = getJpegQualityForLevel(optimizeLevel);
@ -790,10 +797,14 @@ public class CompressController {
log.info("Pre-QPDF file size: {}", GeneralUtils.formatBytes(preQpdfSize));
// Map optimization levels to QPDF compression levels
int qpdfCompressionLevel =
optimizeLevel <= 3
? optimizeLevel * 3 // Level 1->3, 2->6, 3->9
: 9; // Max compression for levels 4-9
int qpdfCompressionLevel;
if (optimizeLevel == 1) {
qpdfCompressionLevel = 5;
} else if (optimizeLevel == 2) {
qpdfCompressionLevel = 9;
} else {
qpdfCompressionLevel = 9;
}
// Create output file for QPDF
Path qpdfOutputFile = Files.createTempFile("qpdf_output_", ".pdf");

View File

@ -622,8 +622,8 @@ public class GetInfoOnPDF {
permissionsNode.put("Document Assembly", getPermissionState(ap.canAssembleDocument()));
permissionsNode.put("Extracting Content", getPermissionState(ap.canExtractContent()));
permissionsNode.put(
"Extracting for accessibility",
getPermissionState(ap.canExtractForAccessibility()));
"Extracting for accessibility",
getPermissionState(ap.canExtractForAccessibility()));
permissionsNode.put("Form Filling", getPermissionState(ap.canFillInForm()));
permissionsNode.put("Modifying", getPermissionState(ap.canModify()));
permissionsNode.put("Modifying annotations", getPermissionState(ap.canModifyAnnotations()));

View File

@ -63,25 +63,25 @@ public class PasswordController {
String ownerPassword = request.getOwnerPassword();
String password = request.getPassword();
int keyLength = request.getKeyLength();
boolean canAssembleDocument = request.isCanAssembleDocument();
boolean canExtractContent = request.isCanExtractContent();
boolean canExtractForAccessibility = request.isCanExtractForAccessibility();
boolean canFillInForm = request.isCanFillInForm();
boolean canModify = request.isCanModify();
boolean canModifyAnnotations = request.isCanModifyAnnotations();
boolean canPrint = request.isCanPrint();
boolean canPrintFaithful = request.isCanPrintFaithful();
boolean preventAssembly = request.isPreventAssembly();
boolean preventExtractContent = request.isPreventExtractContent();
boolean preventExtractForAccessibility = request.isPreventExtractForAccessibility();
boolean preventFillInForm = request.isPreventFillInForm();
boolean preventModify = request.isPreventModify();
boolean preventModifyAnnotations = request.isPreventModifyAnnotations();
boolean preventPrinting = request.isPreventPrinting();
boolean preventPrintingFaithful = request.isPreventPrintingFaithful();
PDDocument document = pdfDocumentFactory.load(fileInput);
AccessPermission ap = new AccessPermission();
ap.setCanAssembleDocument(!canAssembleDocument);
ap.setCanExtractContent(!canExtractContent);
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
ap.setCanFillInForm(!canFillInForm);
ap.setCanModify(!canModify);
ap.setCanModifyAnnotations(!canModifyAnnotations);
ap.setCanPrint(!canPrint);
ap.setCanPrintFaithful(!canPrintFaithful);
ap.setCanAssembleDocument(!preventAssembly);
ap.setCanExtractContent(!preventExtractContent);
ap.setCanExtractForAccessibility(!preventExtractForAccessibility);
ap.setCanFillInForm(!preventFillInForm);
ap.setCanModify(!preventModify);
ap.setCanModifyAnnotations(!preventModifyAnnotations);
ap.setCanPrint(!preventPrinting);
ap.setCanPrintFaithful(!preventPrintingFaithful);
StandardProtectionPolicy spp = new StandardProtectionPolicy(ownerPassword, password, ap);
if (!"".equals(ownerPassword) || !"".equals(password)) {

View File

@ -170,16 +170,17 @@ public class RedactController {
}
private Color decodeOrDefault(String hex, Color defaultColor) {
Color color = null;
try {
color = Color.decode(hex);
if (hex != null && !hex.startsWith("#")) {
hex = "#" + hex;
}
return Color.decode(hex);
} catch (Exception e) {
color = defaultColor;
return defaultColor;
}
return color;
}
private List<Integer> getPageNumbers(ManualRedactPdfRequest request, int pagesCount) {
String pageNumbersInput = request.getPageNumbers();
String[] parsedPageNumbers =

View File

@ -29,31 +29,29 @@ public class AddPasswordRequest extends PDFFile {
defaultValue = "256")
private int keyLength = 256;
@Schema(description = "Whether the document assembly is allowed", example = "false")
private boolean canAssembleDocument;
@Schema(description = "Whether document assembly is prevented", example = "false")
private boolean preventAssembly;
@Schema(description = "Whether content extraction is prevented", example = "false")
private boolean preventExtractContent;
@Schema(
description = "Whether content extraction for accessibility is allowed",
description = "Whether content extraction for accessibility is prevented",
example = "false")
private boolean canExtractContent;
private boolean preventExtractForAccessibility;
@Schema(
description = "Whether content extraction for accessibility is allowed",
example = "false")
private boolean canExtractForAccessibility;
@Schema(description = "Whether form filling is prevented", example = "false")
private boolean preventFillInForm;
@Schema(description = "Whether form filling is allowed", example = "false")
private boolean canFillInForm;
@Schema(description = "Whether document modification is prevented", example = "false")
private boolean preventModify;
@Schema(description = "Whether the document modification is allowed", example = "false")
private boolean canModify;
@Schema(description = "Whether modification of annotations is prevented", example = "false")
private boolean preventModifyAnnotations;
@Schema(description = "Whether modification of annotations is allowed", example = "false")
private boolean canModifyAnnotations;
@Schema(description = "Whether printing of the document is prevented", example = "false")
private boolean preventPrinting;
@Schema(description = "Whether printing of the document is allowed", example = "false")
private boolean canPrint;
@Schema(description = "Whether faithful printing is allowed", example = "false")
private boolean canPrintFaithful;
@Schema(description = "Whether faithful printing is prevented", example = "false")
private boolean preventPrintingFaithful;
}

View File

@ -23,7 +23,7 @@ public class RedactPdfRequest extends PDFFile {
@Schema(description = "Whether to use whole word search", defaultValue = "false")
private boolean wholeWordSearch;
@Schema(description = "The color for redaction", defaultValue = "#000000")
@Schema(description = "Hexadecimal color code for redaction, e.g. #FF0000 or 000000", defaultValue = "#000000")
private String redactColor = "#000000";
@Schema(description = "Custom padding for redaction", type = "number")

View File

@ -517,13 +517,13 @@ home.showJS.title=Afficher le JavaScript
home.showJS.desc=Recherche et affiche tout JavaScript injecté dans un PDF.
showJS.tags=JS
home.autoRedact.title=Censure automatique
home.autoRedact.desc=Censurer automatiquement les informations sensibles d'un PDF.
autoRedact.tags=caviarder,rédiger,censurer,redact,auto
home.autoRedact.title=Caviardage automatique
home.autoRedact.desc=Caviardez automatiquement les informations sensibles d'un PDF.
autoRedact.tags=caviarder,redact,auto,Masquer,noircir,noir,marqueur,caché,rédiger,censurer
home.redact.title=Censure manuelle
home.redact.desc=Censurer un PDF en fonction de texte sélectionné, formes dessinées et/ou des pages sélectionnées.
redact.tags=Redact,Hide,black out,black,marker,hidden,manual,caviarder,rédiger,censurer
home.redact.title=Caviardage manuel
home.redact.desc=Caviarder un PDF en fonction de texte sélectionné, formes dessinées et/ou des pages sélectionnées.
redact.tags=Caviarder,Redact,Masquer,noircir,noir,marqueur,caché,rédiger,censurer
home.tableExtraxt.title=PDF en CSV
home.tableExtraxt.desc=Extrait les tableaux d'un PDF et les transforme en CSV.
@ -624,31 +624,31 @@ autoRedact.convertPDFToImageLabel=Convertir un PDF en PDF-Image (utilisé pour s
autoRedact.submitButton=Caviarder
#redact
redact.title=Rédaction manuelle
redact.header=Rédaction manuelle
redact.submit=Rédiger
redact.textBasedRedaction=Rédaction en fonction de texte
redact.pageBasedRedaction=Rédaction en fonction de pages
redact.title=Caviardage manuel
redact.header=Caviardage manuel
redact.submit=Caviarder
redact.textBasedRedaction=Caviarder du texte
redact.pageBasedRedaction=Caviarder des pages
redact.convertPDFToImageLabel=Convertir en PDF-Image (pour supprimer le texte derrière le rectangle)
redact.pageRedactionNumbers.title=Pages
redact.pageRedactionNumbers.placeholder=(ex: 1,2,8 ou 4,7,12-16 ou 2n-1)
redact.redactionColor.title=Couleur
redact.export=Exporter
redact.upload=Téléverser
redact.boxRedaction=Dessiner le rectangle à rédiger
redact.boxRedaction=Tracer le rectangle à caviarder
redact.zoom=Zoom
redact.zoomIn=Zoom avant
redact.zoomOut=Zoom arrière
redact.nextPage=Page suivante
redact.previousPage=Page précédente
redact.toggleSidebar=Toggle Sidebar
redact.toggleSidebar=Montrer la barre latérale
redact.showThumbnails=Afficher les miniatures
redact.showDocumentOutline=Montrer les contours du document (double-click pour agrandir/réduire tous les éléments)
redact.showAttatchments=Montrer les éléments attachés
redact.showLayers=Montrer les calques (double-click pour réinitialiser tous les calques à l'état par défaut)
redact.colourPicker=Sélection de couleur
redact.findCurrentOutlineItem=Trouver l'élément de contour courrant
redact.applyChanges=Apply Changes
redact.applyChanges=Appliquer les changements
#showJS
showJS.title=Afficher le JavaScript

File diff suppressed because it is too large Load Diff

View File

@ -77,7 +77,7 @@ premium:
appId: ''
mail:
enabled: true # set to 'true' to enable sending emails
enabled: false # set to 'true' to enable sending emails
host: smtp.example.com # SMTP server hostname
port: 587 # SMTP server port
username: '' # SMTP server username
@ -85,7 +85,7 @@ mail:
from: '' # sender email address
legal:
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
termsAndConditions: https://www.stirlingpdf.com/terms # 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
@ -113,11 +113,11 @@ system:
name: postgres # set the name of your database. Should match the name of the database you create
customPaths:
pipeline:
watchedFoldersDir: '' #Defaults to /pipeline/watchedFolders
finishedFoldersDir: '' #Defaults to /pipeline/finishedFolders
watchedFoldersDir: '' # Defaults to /pipeline/watchedFolders
finishedFoldersDir: '' # Defaults to /pipeline/finishedFolders
operations:
weasyprint: '' #Defaults to /opt/venv/bin/weasyprint
unoconvert: '' #Defaults to /opt/venv/bin/unoconvert
weasyprint: '' # Defaults to /opt/venv/bin/weasyprint
unoconvert: '' # Defaults to /opt/venv/bin/unoconvert
fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB".
ui:

View File

@ -1,7 +1,8 @@
#page-container {
min-height: 100vh;
height: 100vh;
display: flex;
flex-direction: column;
overflow-x: clip;
}
#content-wrap {

View File

@ -162,6 +162,7 @@ html[dir="rtl"] .lang-dropdown-item-wrapper {
text-wrap: wrap;
word-break: break-word;
width: 80%;
font-size: large;
}
.close-icon {
@ -476,6 +477,9 @@ html[dir="rtl"] .dropdown-menu[data-bs-popper] {
display: flex;
gap: 30px;
justify-content: center;
width: 140%;
position: relative;
left: -20%;
}
.feature-group {

View File

@ -4,10 +4,21 @@ document.addEventListener('DOMContentLoaded', function () {
if (window.analyticsPromptBoolean) {
const analyticsModal = new bootstrap.Modal(document.getElementById('analyticsModal'));
analyticsModal.show();
let retryCount = 0;
function hideCookieBanner() {
const cookieBanner = document.querySelector('#cc-main');
if (cookieBanner && cookieBanner.offsetHeight > 0) {
cookieBanner.style.display = "none";
} else if (retryCount < 20) {
retryCount++;
setTimeout(hideCookieBanner, 100);
}
}
hideCookieBanner();
}
});
/*]]>*/
function setAnalytics(enabled) {
/*]]>*/function setAnalytics(enabled) {
fetchWithCsrf('api/v1/settings/update-enable-analytics', {
method: 'POST',
headers: {
@ -19,6 +30,15 @@ function setAnalytics(enabled) {
if (response.status === 200) {
console.log('Analytics setting updated successfully');
bootstrap.Modal.getInstance(document.getElementById('analyticsModal')).hide();
if (typeof CookieConsent !== "undefined") {
if (enabled) {
CookieConsent.acceptCategory(['analytics']);
} else {
CookieConsent.acceptCategory([]);
}
}
} else if (response.status === 208) {
console.log('Analytics setting has already been set. Please edit /config/settings.yml to change it.', response);
alert('Analytics setting has already been set. Please edit /config/settings.yml to change it.');

View File

@ -299,21 +299,42 @@ document.getElementById("addOperationBtn").addEventListener("click", function ()
}
}
listItem.innerHTML = `
<div class="d-flex justify-content-between align-items-center w-100">
<div class="operationName">${selectedOperation}</div>
<div class="arrows d-flex">
<button class="btn btn-secondary move-up ms-1"><span class="material-symbols-rounded">arrow_upward</span></button>
<button class="btn btn-secondary move-down ms-1"><span class="material-symbols-rounded">arrow_downward</span></button>
<button class="btn ${hasSettings ? "btn-warning" : "btn-secondary"} pipelineSettings ms-1" ${
hasSettings ? "" : "disabled"
}>
<span class="material-symbols-rounded">settings</span>
</button>
<button class="btn btn-danger remove ms-1"><span class="material-symbols-rounded">close</span></button>
</div>
</div>
`;
let containerDiv = document.createElement("div");
containerDiv.className = "d-flex justify-content-between align-items-center w-100";
let operationNameDiv = document.createElement("div");
operationNameDiv.className = "operationName";
operationNameDiv.textContent = selectedOperation;
containerDiv.appendChild(operationNameDiv);
let arrowsDiv = document.createElement("div");
arrowsDiv.className = "arrows d-flex";
let moveUpButton = document.createElement("button");
moveUpButton.className = "btn btn-secondary move-up ms-1";
moveUpButton.innerHTML = '<span class="material-symbols-rounded">arrow_upward</span>';
arrowsDiv.appendChild(moveUpButton);
let moveDownButton = document.createElement("button");
moveDownButton.className = "btn btn-secondary move-down ms-1";
moveDownButton.innerHTML = '<span class="material-symbols-rounded">arrow_downward</span>';
arrowsDiv.appendChild(moveDownButton);
let settingsButton = document.createElement("button");
settingsButton.className = `btn ${hasSettings ? "btn-warning" : "btn-secondary"} pipelineSettings ms-1`;
if (!hasSettings) {
settingsButton.disabled = true;
}
settingsButton.innerHTML = '<span class="material-symbols-rounded">settings</span>';
arrowsDiv.appendChild(settingsButton);
let removeButton = document.createElement("button");
removeButton.className = "btn btn-danger remove ms-1";
removeButton.innerHTML = '<span class="material-symbols-rounded">close</span>';
arrowsDiv.appendChild(removeButton);
containerDiv.appendChild(arrowsDiv);
listItem.appendChild(containerDiv);
pipelineList.appendChild(listItem);

View File

@ -9,7 +9,7 @@ TabContainer = {
tabList.classList.add('tab-buttons');
tabTitles.forEach((title) => {
const tabButton = document.createElement('button');
tabButton.innerHTML = title;
tabButton.textContent = title;
tabButton.onclick = (e) => {
this.setActiveTab(e.target);
};

View File

@ -34,16 +34,32 @@
<span class="tool-header-text" th:text="#{adminUserSettings.header}">Admin User Control Settings</span>
</div>
<!-- User Settings Title -->
<div
style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
<a href="#" th:data-bs-toggle="${totalUsers >= maxPaidUsers} ? null : 'modal'"
th:data-bs-target="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : '#addUserModal'"
th:class="${totalUsers >= maxPaidUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
th:title="${totalUsers >= maxPaidUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
<span class="material-symbols-rounded">person_add</span>
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
</a>
<!-- User Settings Title -->
<div style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
<a href="#"
th:data-bs-toggle="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : 'modal'"
th:data-bs-target="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : '#addUserModal'"
th:class="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
th:title="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
<span class="material-symbols-rounded">person_add</span>
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
</a>
<a href="#"
data-bs-toggle="modal"
data-bs-target="#changeUserRoleModal"
class="btn btn-outline-success"
th:title="#{adminUserSettings.changeUserRole}">
<span class="material-symbols-rounded">edit</span>
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
</a>
<a th:href="@{'/usage'}" th:if="${@runningEE}"
class="btn btn-outline-success"
th:title="#{adminUserSettings.usage}">
<span class="material-symbols-rounded">analytics</span>
<span th:text="#{adminUserSettings.usage}">Usage Statistics</span>
</a>
<a href="#" data-bs-toggle="modal" data-bs-target="#changeUserRoleModal" class="btn btn-outline-success"
th:title="#{adminUserSettings.changeUserRole}">
@ -400,10 +416,8 @@
checkboxContainer.slideDown('fast');
}
});
});
</script>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
</script>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -41,8 +41,14 @@
<link rel="stylesheet" th:href="@{'/css/bootstrap-icons.min.css'}">
<!-- Pixel, doesn't collect any PII-->
<img referrerpolicy="no-referrer-when-downgrade" src="https://pixel.stirlingpdf.com/a.png?x-pxid=4f5fa02f-a065-4efb-bb2c-24509a4b6b92" style="position: absolute; visibility: hidden;"/>
<img th:if="${!@disablePixel}" referrerpolicy="no-referrer-when-downgrade"
th:src="'https://pixel.stirlingpdf.com/a.png?x-pxid=4f5fa02f-a065-4efb-bb2c-24509a4b6b92'
+ '&machineType=' + ${@machineType}
+ '&appVersion=' + ${@appVersion}
+ '&licenseType=' + ${@license}
+ '&loginEnabled=' + ${@loginEnabled}"
style="position: absolute; visibility: hidden;" />
<!-- Custom -->
<link rel="stylesheet" th:href="@{'/css/general.css'}">
<link rel="stylesheet" th:href="@{'/css/theme/theme.css'}">

View File

@ -1,43 +1,44 @@
<th:block th:fragment="langs">
<div th:replace="~{fragments/languageEntry :: languageEntry ('bg_BG', 'Български')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ar_AR', 'العربية')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ca_CA', 'Català')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('zh_CN', '简体中文')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('zh_TW', '繁體中文')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('zh_BO', 'བོད་ཡིག')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('az_AZ', 'Azərbaycan Dili')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('da_DK', 'Dansk')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('de_DE', 'Deutsch')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('en_GB', 'English (GB)')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('en_US', 'English (US)')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('eu_ES', 'Euskara')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('es_ES', 'Español')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('fr_FR', 'Français')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('id_ID', 'Bahasa Indonesia')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ga_IE', 'Gaeilge')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('it_IT', 'Italiano')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('nl_NL', 'Nederlands')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('fa_IR', 'پارسی')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('pl_PL', 'Polski')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('pt_BR', 'Português (BR)')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('pt_PT', 'Português (PT)')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ro_RO', 'Română')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sk_SK', 'Slovenčina')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sl_SI', 'Slovenščina')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sv_SE', 'Svenska')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('tr_TR', 'Türkçe')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ru_RU', 'Русский')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ko_KR', '한국어')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ja_JP', '日本語')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('el_GR', 'Ελληνικά')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('hu_HU', 'Magyar')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('hi_IN', 'हिन्दी')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sr_LATN_RS', 'Srpski')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('uk_UA', 'Українська')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('cs_CZ', 'Česky')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('hr_HR', 'Hrvatski')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('no_NB', 'Norsk')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('th_TH', 'ไทย')}" ></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('bg_BG', 'Български')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ar_AR', 'العربية')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ca_CA', 'Català')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('zh_CN', '简体中文')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('zh_TW', '繁體中文')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('zh_BO', 'བོད་ཡིག')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('az_AZ', 'Azərbaycan Dili')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('da_DK', 'Dansk')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('de_DE', 'Deutsch')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('en_GB', 'English (GB)')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('en_US', 'English (US)')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('eu_ES', 'Euskara')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('es_ES', 'Español')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('fr_FR', 'Français')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('id_ID', 'Bahasa Indonesia')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ga_IE', 'Gaeilge')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('it_IT', 'Italiano')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('nl_NL', 'Nederlands')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('fa_IR', 'پارسی')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('pl_PL', 'Polski')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('pt_BR', 'Português (BR)')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('pt_PT', 'Português (PT)')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ro_RO', 'Română')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sk_SK', 'Slovenčina')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sl_SI', 'Slovenščina')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sv_SE', 'Svenska')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('tr_TR', 'Türkçe')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ru_RU', 'Русский')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ko_KR', '한국어')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ja_JP', '日本語')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('el_GR', 'Ελληνικά')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('hu_HU', 'Magyar')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('hi_IN', 'हिन्दी')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('sr_LATN_RS', 'Srpski')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('uk_UA', 'Українська')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('cs_CZ', 'Česky')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('hr_HR', 'Hrvatski')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('no_NB', 'Norsk')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('th_TH', 'ไทย')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('vi_VN', 'Tiếng Việt')}"></div>
<div th:replace="~{fragments/languageEntry :: languageEntry ('ml_IN', 'മലയാളം')}"></div>
</th:block>

View File

@ -1,4 +1,4 @@
<div th:fragment="navbar" class="mx-auto" style="position: sticky; top:0; z-index:10000">
<div th:fragment="navbar" class="mx-auto" style="position: sticky; top:0; z-index:10000; width:100%">
<script th:src="@{'/js/languageSelection.js'}"></script>
<script th:src="@{'/js/navbar.js'}"></script>
<script th:src="@{'/js/additionalLanguageCode.js'}"></script>

View File

@ -7,20 +7,18 @@
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<div id="page-container">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<div style="transform-origin: top;"
id="scale-wrap">
<br class="d-md-none">
<!-- Features -->
<script th:src="@{'/js/homecard.js'}"></script>
<div style="
width: 100%;
display: flex;
flex-direction: column;">
flex-direction: column;"
>
<div>
<br>
<div style="justify-content: center; display: flex;">
@ -45,7 +43,7 @@
th:insert="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'organize')}">
</div>
<div class="newfeature"
th:insert="~{fragments/navbarEntry :: navbarEntry('validate-signature', 'verified', 'home.validateSignature.title', 'home.validateSignature.desc', 'validateSignature.tags', 'security')}">
th:insert="~{fragments/navbarEntry :: navbarEntry('compress-pdf', 'zoom_in_map', 'home.compressPdfs.title', 'home.compressPdfs.desc', 'compressPDFs.tags', 'advance')}">
</div>
</div>
</div>
@ -123,9 +121,10 @@
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
@ -222,7 +221,21 @@
window.showSurvey = /*[[${showSurveyFromDocker}]]*/ true
</script>
<script th:src="@{'/js/pages/home.js'}" th:inline="javascript"></script>
<script>
function applyScale() {
const baseWidth = 1440;
const baseHeight = 1000;
const scaleX = window.innerWidth / baseWidth;
const scaleY = window.innerHeight / baseHeight;
const scale = Math.max(0.9, Math.min(scaleX, scaleY)); // keep aspect ratio, honor minScale
const ui = document.getElementById('scale-wrap');
ui.style.transform = `scale(${scale*0.75})`;
}
window.addEventListener('resize', applyScale);
window.addEventListener('load', applyScale);
</script>
</body>

View File

@ -41,36 +41,36 @@
<div class="mb-3">
<label class="mb-2" th:text="#{addPassword.selectText.5}"></label>
<div class="form-check ms-3">
<input type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label for="canAssembleDocument" th:text="#{addPassword.selectText.6}"></label>
<input type="checkbox" id="preventAssembly" name="preventAssembly">
<label for="preventAssembly" th:text="#{addPassword.selectText.6}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractContent" name="canExtractContent">
<label for="canExtractContent" th:text="#{addPassword.selectText.7}"></label>
<input type="checkbox" id="preventExtractContent" name="preventExtractContent">
<label for="preventExtractContent" th:text="#{addPassword.selectText.7}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label for="canExtractForAccessibility" th:text="#{addPassword.selectText.8}"></label>
<input type="checkbox" id="preventExtractForAccessibility" name="preventExtractForAccessibility">
<label for="preventExtractForAccessibility" th:text="#{addPassword.selectText.8}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canFillInForm" name="canFillInForm">
<label for="canFillInForm" th:text="#{addPassword.selectText.9}"></label>
<input type="checkbox" id="preventFillInForm" name="preventFillInForm">
<label for="preventFillInForm" th:text="#{addPassword.selectText.9}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModify" name="canModify">
<label for="canModify" th:text="#{addPassword.selectText.10}"></label>
<input type="checkbox" id="preventModify" name="preventModify">
<label for="preventModify" th:text="#{addPassword.selectText.10}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label for="canModifyAnnotations" th:text="#{addPassword.selectText.11}"></label>
<input type="checkbox" id="preventModifyAnnotations" name="preventModifyAnnotations">
<label for="preventModifyAnnotations" th:text="#{addPassword.selectText.11}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrint" name="canPrint">
<label for="canPrint" th:text="#{addPassword.selectText.12}"></label>
<input type="checkbox" id="preventPrinting" name="preventPrinting">
<label for="preventPrinting" th:text="#{addPassword.selectText.12}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label for="canPrintFaithful" th:text="#{addPassword.selectText.13}"></label>
<input type="checkbox" id="preventPrintingFaithful" name="preventPrintingFaithful">
<label for="preventPrintingFaithful" th:text="#{addPassword.selectText.13}"></label>
</div>
</div>
<br>

View File

@ -25,36 +25,36 @@
<div class="mb-3">
<label class="mb-2" th:text="#{permissions.selectText.2}"></label>
<div class="form-check ms-3">
<input type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label for="canAssembleDocument" th:text="#{permissions.selectText.3}"></label>
<input type="checkbox" id="preventAssembly" name="preventAssembly">
<label for="preventAssembly" th:text="#{permissions.selectText.3}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractContent" name="canExtractContent">
<label for="canExtractContent" th:text="#{permissions.selectText.4}"></label>
<input type="checkbox" id="preventExtractContent" name="preventExtractContent">
<label for="preventExtractContent" th:text="#{permissions.selectText.4}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label for="canExtractForAccessibility" th:text="#{permissions.selectText.5}"></label>
<input type="checkbox" id="preventExtractForAccessibility" name="preventExtractForAccessibility">
<label for="preventExtractForAccessibility" th:text="#{permissions.selectText.5}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canFillInForm" name="canFillInForm">
<label for="canFillInForm" th:text="#{permissions.selectText.6}"></label>
<input type="checkbox" id="preventFillInForm" name="preventFillInForm">
<label for="preventFillInForm" th:text="#{permissions.selectText.6}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModify" name="canModify">
<label for="canModify" th:text="#{permissions.selectText.7}"></label>
<input type="checkbox" id="preventModify" name="preventModify">
<label for="preventModify" th:text="#{permissions.selectText.7}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label for="canModifyAnnotations" th:text="#{permissions.selectText.8}"></label>
<input type="checkbox" id="preventModifyAnnotations" name="preventModifyAnnotations">
<label for="preventModifyAnnotations" th:text="#{permissions.selectText.8}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrint" name="canPrint">
<label for="canPrint" th:text="#{permissions.selectText.9}"></label>
<input type="checkbox" id="preventPrinting" name="preventPrinting">
<label for="preventPrinting" th:text="#{permissions.selectText.9}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label for="canPrintFaithful" th:text="#{permissions.selectText.10}"></label>
<input type="checkbox" id="preventPrintingFaithful" name="preventPrintingFaithful">
<label for="preventPrintingFaithful" th:text="#{permissions.selectText.10}"></label>
</div>
</div>
<br>

View File

@ -19,7 +19,7 @@
<link rel="stylesheet" th:href="@{'/pdfjs-legacy/css/viewer-redact.css'}">
<script th:src="@{'/pdfjs-legacy/js/viewer.mjs'}" type="module"></script>
<script src='./js/redact.js' type="module"></script>
<script th:src="@{'/js/redact.js'}" type="module"></script>
<link rel="stylesheet" th:href="@{'/css/redact.css'}">
</head>
@ -695,4 +695,4 @@
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</body>
</html>
</html>

View File

@ -11,7 +11,6 @@
# If you want to override with environment parameter follow parameter naming SECURITY_INITIALLOGIN_USERNAME #
#############################################################################################################
security:
enableLogin: false # set to 'true' to enable login
csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production)
@ -62,18 +61,32 @@ security:
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
enterpriseEdition:
enabled: false # set to 'true' to enable enterprise edition
premium:
key: 00000000-0000-0000-0000-000000000000
SSOAutoLogin: false # Enable to auto login to first provided SSO
CustomMetadata:
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 user's username
creator: Stirling-PDF # supports text such as 'Company-PDF'
producer: Stirling-PDF # supports text such as 'Company-PDF'
enabled: false # Enable license key checks for pro/enterprise features
proFeatures:
SSOAutoLogin: false
CustomMetadata:
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 user's username
creator: Stirling-PDF # supports text such as 'Company-PDF'
producer: Stirling-PDF # supports text such as 'Company-PDF'
googleDrive:
enabled: false
clientId: ''
apiKey: ''
appId: ''
mail:
enabled: false # set to 'true' to enable sending emails
host: smtp.example.com # SMTP server hostname
port: 587 # SMTP server port
username: '' # SMTP server username
password: '' # SMTP server password
from: '' # sender email address
legal:
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
termsAndConditions: https://www.stirlingpdf.com/terms # 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
@ -88,6 +101,7 @@ system:
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: true # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
enableUrlToPDF: false # Set to 'true' to enable URL to PDF, INTERNAL ONLY, known security issues, should not be used externally
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
datasource:
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
@ -100,13 +114,12 @@ system:
name: postgres # set the name of your database. Should match the name of the database you create
customPaths:
pipeline:
watchedFoldersDir: "" #Defaults to /pipeline/watchedFolders
finishedFoldersDir: "" #Defaults to /pipeline/finishedFolders
watchedFoldersDir: '' # Defaults to /pipeline/watchedFolders
finishedFoldersDir: '' # Defaults to /pipeline/finishedFolders
operations:
weasyprint: "" #Defaults to /opt/venv/bin/weasyprint
unoconvert: "" #Defaults to /opt/venv/bin/unoconvert
weasyprint: '' # Defaults to /opt/venv/bin/weasyprint
unoconvert: '' # Defaults to /opt/venv/bin/unoconvert
fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB".
ui:
appName: '' # application's visible name