Merge branch 'main' into 0.22.8Clone
This commit is contained in:
@@ -5,6 +5,8 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -62,16 +64,39 @@ public class SPdfApplication {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
|
||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||
app.addInitializers(new ConfigInitializer());
|
||||
Map<String, String> propertyFiles = new HashMap<>();
|
||||
|
||||
// stirling pdf settings file
|
||||
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
||||
app.setDefaultProperties(
|
||||
Collections.singletonMap(
|
||||
"spring.config.additional-location", "file:configs/settings.yml"));
|
||||
propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml");
|
||||
} else {
|
||||
logger.warn(
|
||||
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
||||
}
|
||||
|
||||
// custom javs settings file
|
||||
if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
|
||||
String existing = propertyFiles.getOrDefault("spring.config.additional-location", "");
|
||||
if (!existing.isEmpty()) {
|
||||
existing += ",";
|
||||
}
|
||||
propertyFiles.put(
|
||||
"spring.config.additional-location",
|
||||
existing + "file:configs/custom_settings.yml");
|
||||
} else {
|
||||
logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
|
||||
}
|
||||
|
||||
if (!propertyFiles.isEmpty()) {
|
||||
app.setDefaultProperties(
|
||||
Collections.singletonMap(
|
||||
"spring.config.additional-location",
|
||||
propertyFiles.get("spring.config.additional-location")));
|
||||
}
|
||||
|
||||
app.run(args);
|
||||
|
||||
try {
|
||||
|
||||
@@ -6,18 +6,35 @@ import java.nio.file.Paths;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
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.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.thymeleaf.spring6.SpringTemplateEngine;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
@Lazy
|
||||
public class AppConfig {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
name = "system.customHTMLFiles",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
|
||||
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
|
||||
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
|
||||
return templateEngine;
|
||||
}
|
||||
|
||||
@Bean(name = "loginEnabled")
|
||||
public boolean loginEnabled() {
|
||||
return applicationProperties.getSecurity().getEnableLogin();
|
||||
@@ -85,4 +102,10 @@ public class AppConfig {
|
||||
}
|
||||
return "true".equalsIgnoreCase(installOps);
|
||||
}
|
||||
|
||||
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
|
||||
@Bean(name = "activSecurity")
|
||||
public boolean missingActivSecurity() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
class AppUpdateService {
|
||||
|
||||
@Autowired private ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired(required = false)
|
||||
ShowAdminInterface showAdmin;
|
||||
|
||||
@Bean(name = "shouldShow")
|
||||
@Scope("request")
|
||||
public boolean shouldShow() {
|
||||
boolean showUpdate = applicationProperties.getSystem().getShowUpdate();
|
||||
boolean showAdminResult = (showAdmin != null) ? showAdmin.getShowUpdateOnlyAdmins() : true;
|
||||
return showUpdate && showAdminResult;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@@ -26,12 +26,12 @@ public class ConfigInitializer
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
try {
|
||||
ensureConfigExists();
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to initialize application configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureConfigExists() throws IOException {
|
||||
public void ensureConfigExists() throws IOException, URISyntaxException {
|
||||
// Define the path to the external config directory
|
||||
Path destPath = Paths.get("configs", "settings.yml");
|
||||
|
||||
@@ -51,170 +51,152 @@ public class ConfigInitializer
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If user file exists, we need to merge it with the template from the classpath
|
||||
List<String> templateLines;
|
||||
try (InputStream in =
|
||||
getClass().getClassLoader().getResourceAsStream("settings.yml.template")) {
|
||||
templateLines =
|
||||
new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
Path templatePath =
|
||||
Paths.get(
|
||||
getClass()
|
||||
.getClassLoader()
|
||||
.getResource("settings.yml.template")
|
||||
.toURI());
|
||||
Path userPath = Paths.get("configs", "settings.yml");
|
||||
|
||||
mergeYamlFiles(templateLines, destPath, destPath);
|
||||
List<String> templateLines = Files.readAllLines(templatePath);
|
||||
List<String> userLines =
|
||||
Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>();
|
||||
|
||||
Map<String, String> templateEntries = extractEntries(templateLines);
|
||||
Map<String, String> userEntries = extractEntries(userLines);
|
||||
|
||||
List<String> mergedLines = mergeConfigs(templateLines, templateEntries, userEntries);
|
||||
mergedLines = cleanInvalidYamlEntries(mergedLines);
|
||||
Files.write(userPath, mergedLines);
|
||||
}
|
||||
|
||||
Path customSettingsPath = Paths.get("configs", "custom_settings.yml");
|
||||
if (!Files.exists(customSettingsPath)) {
|
||||
Files.createFile(customSettingsPath);
|
||||
}
|
||||
}
|
||||
private static Map<String, String> extractEntries(List<String> lines) {
|
||||
Map<String, String> entries = new HashMap<>();
|
||||
StringBuilder currentEntry = new StringBuilder();
|
||||
String currentKey = null;
|
||||
int blockIndent = -1;
|
||||
|
||||
public void mergeYamlFiles(List<String> templateLines, Path userFilePath, Path outputPath)
|
||||
throws IOException {
|
||||
List<String> userLines = Files.readAllLines(userFilePath);
|
||||
List<String> mergedLines = new ArrayList<>();
|
||||
boolean insideAutoGenerated = false;
|
||||
boolean beforeFirstKey = true;
|
||||
for (String line : lines) {
|
||||
if (line.trim().isEmpty()) {
|
||||
if (currentKey != null) {
|
||||
currentEntry.append(line).append("\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Function<String, Boolean> isCommented = line -> line.trim().startsWith("#");
|
||||
Function<String, String> extractKey =
|
||||
line -> {
|
||||
String[] parts = line.split(":");
|
||||
return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : "";
|
||||
};
|
||||
|
||||
Function<String, Integer> getIndentationLevel =
|
||||
line -> {
|
||||
int count = 0;
|
||||
for (char ch : line.toCharArray()) {
|
||||
if (ch == ' ') count++;
|
||||
else break;
|
||||
int indentLevel = getIndentationLevel(line);
|
||||
if (line.trim().startsWith("#")) {
|
||||
if (indentLevel <= blockIndent || blockIndent == -1) {
|
||||
if (currentKey != null) {
|
||||
entries.put(currentKey, currentEntry.toString().trim());
|
||||
currentEntry = new StringBuilder();
|
||||
}
|
||||
return count;
|
||||
};
|
||||
currentKey = line.trim().replaceAll("#", "").split(":")[0].trim();
|
||||
blockIndent = indentLevel;
|
||||
}
|
||||
currentEntry.append(line).append("\n");
|
||||
} else if (indentLevel == 0 || indentLevel <= blockIndent) {
|
||||
if (currentKey != null) {
|
||||
entries.put(currentKey, currentEntry.toString().trim());
|
||||
currentEntry = new StringBuilder();
|
||||
}
|
||||
currentKey = line.split(":")[0].trim();
|
||||
blockIndent = indentLevel;
|
||||
currentEntry.append(line).append("\n");
|
||||
} else {
|
||||
currentEntry.append(line).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet());
|
||||
if (currentKey != null) {
|
||||
entries.put(currentKey, currentEntry.toString().trim());
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
private static List<String> mergeConfigs(List<String> templateLines, Map<String, String> templateEntries, Map<String, String> userEntries) {
|
||||
List<String> mergedLines = new ArrayList<>();
|
||||
Set<String> handledKeys = new HashSet<>();
|
||||
|
||||
String currentBlockKey = null;
|
||||
int blockIndent = -1;
|
||||
|
||||
for (String line : templateLines) {
|
||||
String key = extractKey.apply(line);
|
||||
|
||||
if ("AutomaticallyGenerated:".equalsIgnoreCase(line.trim())) {
|
||||
insideAutoGenerated = true;
|
||||
mergedLines.add(line);
|
||||
continue;
|
||||
} else if (insideAutoGenerated && line.trim().isEmpty()) {
|
||||
insideAutoGenerated = false;
|
||||
if (line.trim().isEmpty()) {
|
||||
mergedLines.add(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (beforeFirstKey && (isCommented.apply(line) || line.trim().isEmpty())) {
|
||||
// Handle top comments and empty lines before the first key.
|
||||
int indentLevel = getIndentationLevel(line);
|
||||
if (indentLevel == 0 || (indentLevel <= blockIndent && !line.trim().startsWith("#"))) {
|
||||
currentBlockKey = line.split(":")[0].trim();
|
||||
blockIndent = indentLevel;
|
||||
}
|
||||
|
||||
if (userEntries.containsKey(currentBlockKey) && !handledKeys.contains(currentBlockKey)) {
|
||||
mergedLines.add(userEntries.get(currentBlockKey));
|
||||
handledKeys.add(currentBlockKey);
|
||||
} else if (!handledKeys.contains(currentBlockKey)) {
|
||||
mergedLines.add(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!key.isEmpty()) beforeFirstKey = false;
|
||||
|
||||
if (userKeys.contains(key)) {
|
||||
// If user has any version (commented or uncommented) of this key, skip the
|
||||
// template line
|
||||
Optional<String> userValue =
|
||||
userLines.stream()
|
||||
.filter(
|
||||
l ->
|
||||
extractKey.apply(l).equalsIgnoreCase(key)
|
||||
&& !isCommented.apply(l))
|
||||
.findFirst();
|
||||
if (userValue.isPresent()) mergedLines.add(userValue.get());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isCommented.apply(line) || line.trim().isEmpty() || !userKeys.contains(key)) {
|
||||
mergedLines.add(
|
||||
line); // If line is commented, empty or key not present in user's file,
|
||||
// retain the
|
||||
// template line
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add any additional uncommented user lines that are not present in the
|
||||
// template
|
||||
for (String userLine : userLines) {
|
||||
String userKey = extractKey.apply(userLine);
|
||||
boolean isPresentInTemplate =
|
||||
templateLines.stream()
|
||||
.map(extractKey)
|
||||
.anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey));
|
||||
if (!isPresentInTemplate && !isCommented.apply(userLine)) {
|
||||
if (!childOfTemplateEntry(
|
||||
isCommented,
|
||||
extractKey,
|
||||
getIndentationLevel,
|
||||
userLines,
|
||||
userLine,
|
||||
templateLines)) {
|
||||
// check if userLine is a child of a entry within templateLines or not, if child
|
||||
// of parent in templateLines then dont add to mergedLines, if anything else
|
||||
// then add
|
||||
mergedLines.add(userLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Files.write(outputPath, mergedLines, StandardCharsets.UTF_8);
|
||||
return mergedLines;
|
||||
}
|
||||
|
||||
// New method to check if a userLine is a child of an entry in templateLines
|
||||
boolean childOfTemplateEntry(
|
||||
Function<String, Boolean> isCommented,
|
||||
Function<String, String> extractKey,
|
||||
Function<String, Integer> getIndentationLevel,
|
||||
List<String> userLines,
|
||||
String userLine,
|
||||
List<String> templateLines) {
|
||||
String userKey = extractKey.apply(userLine).trim();
|
||||
int userIndentation = getIndentationLevel.apply(userLine);
|
||||
|
||||
// Start by assuming the line is not a child of an entry in templateLines
|
||||
boolean isChild = false;
|
||||
|
||||
// Iterate backwards through userLines from the current line to find any parent
|
||||
for (int i = userLines.indexOf(userLine) - 1; i >= 0; i--) {
|
||||
String potentialParentLine = userLines.get(i);
|
||||
int parentIndentation = getIndentationLevel.apply(potentialParentLine);
|
||||
private static List<String> cleanInvalidYamlEntries(List<String> lines) {
|
||||
List<String> cleanedLines = new ArrayList<>();
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
String line = lines.get(i);
|
||||
String trimmedLine = line.trim();
|
||||
|
||||
// Check if we've reached a potential parent based on indentation
|
||||
if (parentIndentation < userIndentation) {
|
||||
String parentKey = extractKey.apply(potentialParentLine).trim();
|
||||
|
||||
// Now, check if this potential parent or any of its parents exist in templateLines
|
||||
boolean parentExistsInTemplate =
|
||||
templateLines.stream()
|
||||
.filter(line -> !isCommented.apply(line)) // Skip commented lines
|
||||
.anyMatch(
|
||||
templateLine -> {
|
||||
String templateKey =
|
||||
extractKey.apply(templateLine).trim();
|
||||
return parentKey.equalsIgnoreCase(templateKey);
|
||||
});
|
||||
|
||||
if (!parentExistsInTemplate) {
|
||||
// If the parent does not exist in template, check the next level parent
|
||||
userIndentation =
|
||||
parentIndentation; // Update userIndentation to the parent's indentation
|
||||
// for next iteration
|
||||
if (parentIndentation == 0) {
|
||||
// If we've reached the top-level parent and it's not in template, the
|
||||
// original line is considered not a child
|
||||
isChild = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// If any parent exists in template, the original line is considered a child
|
||||
isChild = true;
|
||||
break;
|
||||
}
|
||||
if (trimmedLine.startsWith("#")
|
||||
|| !trimmedLine.endsWith(":")
|
||||
|| trimmedLine.contains(" ")) {
|
||||
cleanedLines.add(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isKeyWithoutChildrenOrValue(i, lines)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cleanedLines.add(line);
|
||||
}
|
||||
return cleanedLines;
|
||||
}
|
||||
|
||||
private static boolean isKeyWithoutChildrenOrValue(int currentIndex, List<String> lines) {
|
||||
if (currentIndex + 1 < lines.size()) {
|
||||
String currentLine = lines.get(currentIndex);
|
||||
String nextLine = lines.get(currentIndex + 1);
|
||||
int currentIndentation = getIndentationLevel(currentLine);
|
||||
int nextIndentation = getIndentationLevel(nextLine);
|
||||
|
||||
// If the next line is less or equally indented, it's not a child or value
|
||||
return nextIndentation <= currentIndentation;
|
||||
}
|
||||
|
||||
return isChild; // Return true if the line is not a child of any entry in templateLines
|
||||
// If it's the last line, then it definitely has no children or value
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int getIndentationLevel(String line) {
|
||||
int count = 0;
|
||||
for (char ch : line.toCharArray()) {
|
||||
if (ch == ' ') count++;
|
||||
else break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,6 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("CLI", "xlsx-to-pdf");
|
||||
addEndpointToGroup("CLI", "pdf-to-word");
|
||||
addEndpointToGroup("CLI", "pdf-to-presentation");
|
||||
addEndpointToGroup("CLI", "pdf-to-text");
|
||||
addEndpointToGroup("CLI", "pdf-to-html");
|
||||
addEndpointToGroup("CLI", "pdf-to-xml");
|
||||
addEndpointToGroup("CLI", "ocr-pdf");
|
||||
@@ -154,6 +153,7 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("CLI", "url-to-pdf");
|
||||
addEndpointToGroup("CLI", "book-to-pdf");
|
||||
addEndpointToGroup("CLI", "pdf-to-book");
|
||||
addEndpointToGroup("CLI", "pdf-to-rtf");
|
||||
|
||||
// Calibre
|
||||
addEndpointToGroup("Calibre", "book-to-pdf");
|
||||
@@ -175,7 +175,7 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("LibreOffice", "xlsx-to-pdf");
|
||||
addEndpointToGroup("LibreOffice", "pdf-to-word");
|
||||
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
|
||||
addEndpointToGroup("LibreOffice", "pdf-to-text");
|
||||
addEndpointToGroup("LibreOffice", "pdf-to-rtf");
|
||||
addEndpointToGroup("LibreOffice", "pdf-to-html");
|
||||
addEndpointToGroup("LibreOffice", "pdf-to-xml");
|
||||
|
||||
@@ -218,6 +218,7 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Java", "overlay-pdf");
|
||||
addEndpointToGroup("Java", "split-pdf-by-sections");
|
||||
addEndpointToGroup("Java", REMOVE_BLANKS);
|
||||
addEndpointToGroup("Java", "pdf-to-text");
|
||||
|
||||
// Javascript
|
||||
addEndpointToGroup("Javascript", "pdf-organizer");
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.thymeleaf.IEngineConfiguration;
|
||||
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
|
||||
import org.thymeleaf.templateresource.ClassLoaderTemplateResource;
|
||||
import org.thymeleaf.templateresource.FileTemplateResource;
|
||||
import org.thymeleaf.templateresource.ITemplateResource;
|
||||
|
||||
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
public FileFallbackTemplateResolver(ResourceLoader resourceLoader) {
|
||||
super();
|
||||
this.resourceLoader = resourceLoader;
|
||||
setSuffix(".html");
|
||||
}
|
||||
|
||||
// Note this does not work in local IDE, Prod jar only.
|
||||
@Override
|
||||
protected ITemplateResource computeTemplateResource(
|
||||
IEngineConfiguration configuration,
|
||||
String ownerTemplate,
|
||||
String template,
|
||||
String resourceName,
|
||||
String characterEncoding,
|
||||
Map<String, Object> templateResolutionAttributes) {
|
||||
Resource resource =
|
||||
resourceLoader.getResource("file:./customFiles/templates/" + resourceName);
|
||||
try {
|
||||
if (resource.exists() && resource.isReadable()) {
|
||||
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
|
||||
return new ClassLoaderTemplateResource(
|
||||
Thread.currentThread().getContextClassLoader(),
|
||||
"classpath:/templates/" + resourceName,
|
||||
characterEncoding);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
public interface ShowAdminInterface {
|
||||
default boolean getShowUpdateOnlyAdmins() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.config.ShowAdminInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
|
||||
@Service
|
||||
class AppUpdateAuthService implements ShowAdminInterface {
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
@Autowired private ApplicationProperties applicationProperties;
|
||||
|
||||
public boolean getShowUpdateOnlyAdmins() {
|
||||
boolean showUpdate = applicationProperties.getSystem().getShowUpdate();
|
||||
if (!showUpdate) {
|
||||
return showUpdate;
|
||||
}
|
||||
|
||||
boolean showUpdateOnlyAdmin = applicationProperties.getSystem().getShowUpdateOnlyAdmin();
|
||||
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
return !showUpdateOnlyAdmin;
|
||||
}
|
||||
|
||||
if (authentication.getName().equalsIgnoreCase("anonymousUser")) {
|
||||
return !showUpdateOnlyAdmin;
|
||||
}
|
||||
|
||||
Optional<User> user = userRepository.findByUsername(authentication.getName());
|
||||
if (user.isPresent() && showUpdateOnlyAdmin) {
|
||||
return "ROLE_ADMIN".equals(user.get().getRolesAsString());
|
||||
}
|
||||
|
||||
return showUpdate;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
||||
}
|
||||
|
||||
private boolean isDemoUser(String username) {
|
||||
Optional<User> user = userService.findByUsername(username);
|
||||
Optional<User> user = userService.findByUsernameIgnoreCase(username);
|
||||
return user.isPresent()
|
||||
&& user.get().getAuthorities().stream()
|
||||
.anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority()));
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||
@Bean
|
||||
public SessionRegistry sessionRegistry() {
|
||||
return new SessionRegistryImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(
|
||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
String sessionId = session.getId();
|
||||
sessionRegistry().removeSessionInformation(sessionId);
|
||||
}
|
||||
|
||||
if (request.getParameter("oauth2AutoCreateDisabled") != null) {
|
||||
response.sendRedirect(
|
||||
request.getContextPath() + "/login?error=oauth2AutoCreateDisabled");
|
||||
} else {
|
||||
response.sendRedirect(request.getContextPath() + "/login?logout=true");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class FirstLoginFilter extends OncePerRequestFilter {
|
||||
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
Optional<User> user = userService.findByUsername(authentication.getName());
|
||||
Optional<User> user = userService.findByUsernameIgnoreCase(authentication.getName());
|
||||
if ("GET".equalsIgnoreCase(method)
|
||||
&& user.isPresent()
|
||||
&& user.get().isFirstLogin()
|
||||
|
||||
@@ -38,7 +38,7 @@ public class InitialSecuritySetup {
|
||||
initialUsername, initialPassword, Role.ADMIN.getRoleId(), true);
|
||||
}
|
||||
}
|
||||
if (!userService.usernameExists(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
userService.saveUser(
|
||||
Role.INTERNAL_API_USER.getRoleId(),
|
||||
UUID.randomUUID().toString(),
|
||||
@@ -50,7 +50,7 @@ public class InitialSecuritySetup {
|
||||
@PostConstruct
|
||||
public void initSecretKey() throws IOException {
|
||||
String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
|
||||
if (secretKey == null || secretKey.isEmpty()) {
|
||||
if (!isValidUUID(secretKey)) {
|
||||
secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
|
||||
saveKeyToConfig(secretKey);
|
||||
}
|
||||
@@ -85,4 +85,16 @@ public class InitialSecuritySetup {
|
||||
// Write back to the file
|
||||
Files.write(path, lines);
|
||||
}
|
||||
private boolean isValidUUID(String uuid) {
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
UUID.fromString(uuid);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
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;
|
||||
@@ -10,18 +14,34 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
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 jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||
|
||||
@Configuration
|
||||
@@ -42,6 +62,8 @@ public class SecurityConfiguration {
|
||||
@Qualifier("loginEnabled")
|
||||
public boolean loginEnabledValue;
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired private UserAuthenticationFilter userAuthenticationFilter;
|
||||
|
||||
@Autowired private LoginAttemptService loginAttemptService;
|
||||
@@ -87,7 +109,7 @@ public class SecurityConfiguration {
|
||||
logout ->
|
||||
logout.logoutRequestMatcher(
|
||||
new AntPathRequestMatcher("/logout"))
|
||||
.logoutSuccessUrl("/login?logout=true")
|
||||
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
|
||||
.invalidateHttpSession(true) // Invalidate session
|
||||
.deleteCookies("JSESSIONID", "remember-me")
|
||||
.addLogoutHandler(
|
||||
@@ -124,6 +146,7 @@ public class SecurityConfiguration {
|
||||
: uri;
|
||||
|
||||
return trimmedUri.startsWith("/login")
|
||||
|| trimmedUri.startsWith("/oauth")
|
||||
|| trimmedUri.endsWith(".svg")
|
||||
|| trimmedUri.startsWith(
|
||||
"/register")
|
||||
@@ -140,6 +163,49 @@ public class SecurityConfiguration {
|
||||
.authenticated())
|
||||
.userDetailsService(userDetailsService)
|
||||
.authenticationProvider(authenticationProvider());
|
||||
|
||||
// Handle OAUTH2 Logins
|
||||
if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
|
||||
|
||||
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.
|
||||
*/
|
||||
.successHandler(
|
||||
new AuthenticationSuccessHandler() {
|
||||
@Override
|
||||
public void onAuthenticationSuccess(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Authentication authentication)
|
||||
throws ServletException, IOException {
|
||||
OAuth2User oauthUser =
|
||||
(OAuth2User)
|
||||
authentication
|
||||
.getPrincipal();
|
||||
if (userService.processOAuth2PostLogin(
|
||||
oauthUser.getAttribute("email"),
|
||||
applicationProperties
|
||||
.getSecurity()
|
||||
.getOAUTH2()
|
||||
.getAutoCreateUser())) {
|
||||
response.sendRedirect("/");
|
||||
} else {
|
||||
response.sendRedirect(
|
||||
"/logout?oauth2AutoCreateDisabled=true");
|
||||
}
|
||||
}
|
||||
})
|
||||
// Add existing Authorities from the database
|
||||
.userInfoEndpoint(
|
||||
userInfoEndpoint ->
|
||||
userInfoEndpoint.userAuthoritiesMapper(
|
||||
userAuthoritiesMapper())));
|
||||
}
|
||||
} else {
|
||||
http.csrf(csrf -> csrf.disable())
|
||||
.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
||||
@@ -148,6 +214,65 @@ public class SecurityConfiguration {
|
||||
return http.build();
|
||||
}
|
||||
|
||||
// Client Registration Repository for OAUTH2 OIDC Login
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
value = "security.oauth2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||
return new InMemoryClientRegistrationRepository(this.oidcClientRegistration());
|
||||
}
|
||||
|
||||
private ClientRegistration oidcClientRegistration() {
|
||||
return ClientRegistrations.fromOidcIssuerLocation(
|
||||
applicationProperties.getSecurity().getOAUTH2().getIssuer())
|
||||
.registrationId("oidc")
|
||||
.clientId(applicationProperties.getSecurity().getOAUTH2().getClientId())
|
||||
.clientSecret(applicationProperties.getSecurity().getOAUTH2().getClientSecret())
|
||||
.scope("openid", "profile", "email")
|
||||
.userNameAttributeName("email")
|
||||
.clientName("OIDC")
|
||||
.build();
|
||||
}
|
||||
|
||||
/*
|
||||
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
|
||||
This is required for the internal; 'hasRole()' function to give out the correct role.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
value = "security.oauth2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
||||
return (authorities) -> {
|
||||
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
||||
|
||||
authorities.forEach(
|
||||
authority -> {
|
||||
// Add existing OAUTH2 Authorities
|
||||
mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
|
||||
|
||||
// Add Authorities from database for existing user, if user is present.
|
||||
if (authority instanceof OAuth2UserAuthority oauth2Auth) {
|
||||
Optional<User> userOpt =
|
||||
userService.findByUsernameIgnoreCase(
|
||||
(String) oauth2Auth.getAttributes().get("email"));
|
||||
if (userOpt.isPresent()) {
|
||||
User user = userOpt.get();
|
||||
if (user != null) {
|
||||
mappedAuthorities.add(
|
||||
new SimpleGrantedAuthority(
|
||||
userService.findRole(user).getAuthority()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return mappedAuthorities;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IPRateLimitingFilter rateLimitingFilter() {
|
||||
int maxRequestsPerIp = 1000000; // Example limit TODO add config level
|
||||
@@ -166,4 +291,9 @@ public class SecurityConfiguration {
|
||||
public PersistentTokenRepository persistentTokenRepository() {
|
||||
return new JPATokenRepositoryImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public boolean activSecurity() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||
import stirling.software.SPDF.model.Authority;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.repository.AuthorityRepository;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
|
||||
@Service
|
||||
@@ -28,8 +29,28 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
|
||||
@Autowired private AuthorityRepository authorityRepository;
|
||||
|
||||
@Autowired private PasswordEncoder passwordEncoder;
|
||||
|
||||
// Handle OAUTH2 login and user auto creation.
|
||||
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) {
|
||||
Optional<User> existUser = userRepository.findByUsernameIgnoreCase(username);
|
||||
if (existUser.isPresent()) {
|
||||
return true;
|
||||
}
|
||||
if (autoCreateUser) {
|
||||
User user = new User();
|
||||
user.setUsername(username);
|
||||
user.setEnabled(true);
|
||||
user.setFirstLogin(false);
|
||||
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
|
||||
userRepository.save(user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Authentication getAuthentication(String apiKey) {
|
||||
User user = getUserByApiKey(apiKey);
|
||||
if (user == null) {
|
||||
@@ -62,7 +83,7 @@ public class UserService implements UserServiceInterface {
|
||||
public User addApiKeyToUser(String username) {
|
||||
User user =
|
||||
userRepository
|
||||
.findByUsername(username)
|
||||
.findByUsernameIgnoreCase(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
|
||||
user.setApiKey(generateApiKey());
|
||||
@@ -76,7 +97,7 @@ public class UserService implements UserServiceInterface {
|
||||
public String getApiKeyForUser(String username) {
|
||||
User user =
|
||||
userRepository
|
||||
.findByUsername(username)
|
||||
.findByUsernameIgnoreCase(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
return user.getApiKey();
|
||||
}
|
||||
@@ -103,7 +124,7 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public boolean validateApiKeyForUser(String username, String apiKey) {
|
||||
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
||||
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey);
|
||||
}
|
||||
|
||||
@@ -136,7 +157,7 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public void deleteUser(String username) {
|
||||
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
||||
if (userOpt.isPresent()) {
|
||||
for (Authority authority : userOpt.get().getAuthorities()) {
|
||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
@@ -151,12 +172,16 @@ public class UserService implements UserServiceInterface {
|
||||
return userRepository.findByUsername(username).isPresent();
|
||||
}
|
||||
|
||||
public boolean usernameExistsIgnoreCase(String username) {
|
||||
return userRepository.findByUsernameIgnoreCase(username).isPresent();
|
||||
}
|
||||
|
||||
public boolean hasUsers() {
|
||||
return userRepository.count() > 0;
|
||||
}
|
||||
|
||||
public void updateUserSettings(String username, Map<String, String> updates) {
|
||||
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
||||
if (userOpt.isPresent()) {
|
||||
User user = userOpt.get();
|
||||
Map<String, String> settingsMap = user.getSettings();
|
||||
@@ -180,6 +205,10 @@ public class UserService implements UserServiceInterface {
|
||||
return userRepository.findByUsernameIgnoreCase(username);
|
||||
}
|
||||
|
||||
public Authority findRole(User user) {
|
||||
return authorityRepository.findByUserId(user.getId());
|
||||
}
|
||||
|
||||
public void changeUsername(User user, String newUsername) {
|
||||
user.setUsername(newUsername);
|
||||
userRepository.save(user);
|
||||
@@ -195,6 +224,12 @@ public class UserService implements UserServiceInterface {
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
public void changeRole(User user, String newRole) {
|
||||
Authority userAuthority = this.findRole(user);
|
||||
userAuthority.setAuthority(newRole);
|
||||
authorityRepository.save(userAuthority);
|
||||
}
|
||||
|
||||
public boolean isPasswordCorrect(User user, String currentPassword) {
|
||||
return passwordEncoder.matches(currentPassword, user.getPassword());
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ 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.PdfMetadata;
|
||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -49,6 +51,7 @@ public class SplitPDFController {
|
||||
// open the pdf document
|
||||
|
||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||
PdfMetadata metadata = PdfUtils.extractMetadataFromPdf(document);
|
||||
int totalPages = document.getNumberOfPages();
|
||||
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||
System.out.println(
|
||||
@@ -75,6 +78,9 @@ public class SplitPDFController {
|
||||
}
|
||||
previousPageNumber = splitPoint + 1;
|
||||
|
||||
// Transfer metadata to split pdf
|
||||
PdfUtils.setMetadataToPdf(splitDocument, metadata);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
splitDocument.save(baos);
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@@ -41,117 +39,137 @@ public class SplitPdfBySizeController {
|
||||
+ " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF Output:ZIP-PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request)
|
||||
throws Exception {
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<ByteArrayOutputStream>();
|
||||
|
||||
MultipartFile file = request.getFileInput();
|
||||
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
|
||||
|
||||
// 0 = size, 1 = page count, 2 = doc count
|
||||
int type = request.getSplitType();
|
||||
String value = request.getSplitValue();
|
||||
|
||||
if (type == 0) { // Split by size
|
||||
long maxBytes = GeneralUtils.convertSizeToBytes(value);
|
||||
long currentSize = 0;
|
||||
PDDocument currentDoc = new PDDocument();
|
||||
|
||||
for (PDPage page : sourceDocument.getPages()) {
|
||||
ByteArrayOutputStream pageOutputStream = new ByteArrayOutputStream();
|
||||
PDDocument tempDoc = new PDDocument();
|
||||
tempDoc.addPage(page);
|
||||
tempDoc.save(pageOutputStream);
|
||||
tempDoc.close();
|
||||
|
||||
long pageSize = pageOutputStream.size();
|
||||
if (currentSize + pageSize > maxBytes) {
|
||||
// Save and reset current document
|
||||
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
|
||||
currentDoc = new PDDocument();
|
||||
currentSize = 0;
|
||||
}
|
||||
|
||||
currentDoc.addPage(page);
|
||||
currentSize += pageSize;
|
||||
}
|
||||
// Add the last document if it contains any pages
|
||||
if (currentDoc.getPages().getCount() != 0) {
|
||||
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
|
||||
}
|
||||
} else if (type == 1) { // Split by page count
|
||||
int pageCount = Integer.parseInt(value);
|
||||
int currentPageCount = 0;
|
||||
PDDocument currentDoc = new PDDocument();
|
||||
|
||||
for (PDPage page : sourceDocument.getPages()) {
|
||||
currentDoc.addPage(page);
|
||||
currentPageCount++;
|
||||
|
||||
if (currentPageCount == pageCount) {
|
||||
// Save and reset current document
|
||||
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
|
||||
currentDoc = new PDDocument();
|
||||
currentPageCount = 0;
|
||||
}
|
||||
}
|
||||
// Add the last document if it contains any pages
|
||||
if (currentDoc.getPages().getCount() != 0) {
|
||||
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
|
||||
}
|
||||
} else if (type == 2) { // Split by doc count
|
||||
int documentCount = Integer.parseInt(value);
|
||||
int totalPageCount = sourceDocument.getNumberOfPages();
|
||||
int pagesPerDocument = totalPageCount / documentCount;
|
||||
int extraPages = totalPageCount % documentCount;
|
||||
int currentPageIndex = 0;
|
||||
|
||||
for (int i = 0; i < documentCount; i++) {
|
||||
PDDocument currentDoc = new PDDocument();
|
||||
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
|
||||
|
||||
for (int j = 0; j < pagesToAdd; j++) {
|
||||
currentDoc.addPage(sourceDocument.getPage(currentPageIndex++));
|
||||
}
|
||||
|
||||
splitDocumentsBoas.add(currentDocToByteArray(currentDoc));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid argument for split type");
|
||||
}
|
||||
|
||||
sourceDocument.close();
|
||||
|
||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
byte[] data;
|
||||
byte[] data = null;
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile));
|
||||
PDDocument sourceDocument = Loader.loadPDF(file.getBytes())) {
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||
String fileName = filename + "_" + (i + 1) + ".pdf";
|
||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||
byte[] pdf = baos.toByteArray();
|
||||
int type = request.getSplitType();
|
||||
String value = request.getSplitValue();
|
||||
|
||||
ZipEntry pdfEntry = new ZipEntry(fileName);
|
||||
zipOut.putNextEntry(pdfEntry);
|
||||
zipOut.write(pdf);
|
||||
zipOut.closeEntry();
|
||||
if (type == 0) {
|
||||
long maxBytes = GeneralUtils.convertSizeToBytes(value);
|
||||
handleSplitBySize(sourceDocument, maxBytes, zipOut, filename);
|
||||
} else if (type == 1) {
|
||||
int pageCount = Integer.parseInt(value);
|
||||
handleSplitByPageCount(sourceDocument, pageCount, zipOut, filename);
|
||||
} else if (type == 2) {
|
||||
int documentCount = Integer.parseInt(value);
|
||||
handleSplitByDocCount(sourceDocument, documentCount, zipOut, filename);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid argument for split type");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
data = Files.readAllBytes(zipFile);
|
||||
Files.delete(zipFile);
|
||||
Files.deleteIfExists(zipFile);
|
||||
}
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
private ByteArrayOutputStream currentDocToByteArray(PDDocument document) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
document.save(baos);
|
||||
document.close();
|
||||
return baos;
|
||||
private void handleSplitBySize(
|
||||
PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename)
|
||||
throws IOException {
|
||||
long currentSize = 0;
|
||||
PDDocument currentDoc = new PDDocument();
|
||||
int fileIndex = 1;
|
||||
|
||||
for (int pageIndex = 0; pageIndex < sourceDocument.getNumberOfPages(); pageIndex++) {
|
||||
PDPage page = sourceDocument.getPage(pageIndex);
|
||||
ByteArrayOutputStream pageOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
try (PDDocument tempDoc = new PDDocument()) {
|
||||
PDPage importedPage = tempDoc.importPage(page); // This creates a new PDPage object
|
||||
tempDoc.save(pageOutputStream);
|
||||
}
|
||||
|
||||
long pageSize = pageOutputStream.size();
|
||||
if (currentSize + pageSize > maxBytes) {
|
||||
if (currentDoc.getNumberOfPages() > 0) {
|
||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||
currentDoc.close(); // Make sure to close the document
|
||||
currentDoc = new PDDocument();
|
||||
currentSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PDPage newPage = new PDPage(page.getCOSObject()); // Re-create the page
|
||||
currentDoc.addPage(newPage);
|
||||
currentSize += pageSize;
|
||||
}
|
||||
|
||||
if (currentDoc.getNumberOfPages() != 0) {
|
||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||
currentDoc.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSplitByPageCount(
|
||||
PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename)
|
||||
throws IOException {
|
||||
int currentPageCount = 0;
|
||||
PDDocument currentDoc = new PDDocument();
|
||||
int fileIndex = 1;
|
||||
for (PDPage page : sourceDocument.getPages()) {
|
||||
currentDoc.addPage(page);
|
||||
currentPageCount++;
|
||||
|
||||
if (currentPageCount == pageCount) {
|
||||
// Save and reset current document
|
||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||
currentDoc = new PDDocument();
|
||||
currentPageCount = 0;
|
||||
}
|
||||
}
|
||||
// Add the last document if it contains any pages
|
||||
if (currentDoc.getPages().getCount() != 0) {
|
||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSplitByDocCount(
|
||||
PDDocument sourceDocument,
|
||||
int documentCount,
|
||||
ZipOutputStream zipOut,
|
||||
String baseFilename)
|
||||
throws IOException {
|
||||
int totalPageCount = sourceDocument.getNumberOfPages();
|
||||
int pagesPerDocument = totalPageCount / documentCount;
|
||||
int extraPages = totalPageCount % documentCount;
|
||||
int currentPageIndex = 0;
|
||||
int fileIndex = 1;
|
||||
for (int i = 0; i < documentCount; i++) {
|
||||
PDDocument currentDoc = new PDDocument();
|
||||
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
|
||||
|
||||
for (int j = 0; j < pagesToAdd; j++) {
|
||||
currentDoc.addPage(sourceDocument.getPage(currentPageIndex++));
|
||||
}
|
||||
|
||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDocumentToZip(
|
||||
PDDocument document, ZipOutputStream zipOut, String baseFilename, int index)
|
||||
throws IOException {
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
document.save(outStream);
|
||||
document.close(); // Close the document to free resources
|
||||
|
||||
// Create a new zip entry
|
||||
ZipEntry zipEntry = new ZipEntry(baseFilename + "_" + index + ".pdf");
|
||||
zipOut.putNextEntry(zipEntry);
|
||||
zipOut.write(outStream.toByteArray());
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class UserController {
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/register")
|
||||
public String register(@ModelAttribute UsernameAndPass requestModel, Model model) {
|
||||
if (userService.usernameExists(requestModel.getUsername())) {
|
||||
if (userService.usernameExistsIgnoreCase(requestModel.getUsername())) {
|
||||
model.addAttribute("error", "Username already exists");
|
||||
return "register";
|
||||
}
|
||||
@@ -70,7 +70,8 @@ public class UserController {
|
||||
return new RedirectView("/account?messageType=notAuthenticated");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
|
||||
// The username MUST be unique when renaming
|
||||
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
||||
|
||||
if (userOpt == null || userOpt.isEmpty()) {
|
||||
return new RedirectView("/account?messageType=userNotFound");
|
||||
@@ -113,7 +114,7 @@ public class UserController {
|
||||
return new RedirectView("/change-creds?messageType=notAuthenticated");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
|
||||
|
||||
if (userOpt == null || userOpt.isEmpty()) {
|
||||
return new RedirectView("/change-creds?messageType=userNotFound");
|
||||
@@ -146,7 +147,7 @@ public class UserController {
|
||||
return new RedirectView("/account?messageType=notAuthenticated");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
|
||||
|
||||
if (userOpt == null || userOpt.isEmpty()) {
|
||||
return new RedirectView("/account?messageType=userNotFound");
|
||||
@@ -207,7 +208,7 @@ public class UserController {
|
||||
return new RedirectView("/addUsers?messageType=usernameExists");
|
||||
}
|
||||
}
|
||||
if (userService.usernameExists(username)) {
|
||||
if (userService.usernameExistsIgnoreCase(username)) {
|
||||
return new RedirectView("/addUsers?messageType=usernameExists");
|
||||
}
|
||||
try {
|
||||
@@ -226,12 +227,51 @@ public class UserController {
|
||||
return new RedirectView("/addUsers"); // Redirect to account page after adding the user
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/admin/changeRole")
|
||||
public RedirectView changeRole(
|
||||
@RequestParam(name = "username") String username,
|
||||
@RequestParam(name = "role") String role,
|
||||
Authentication authentication) {
|
||||
|
||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||
|
||||
if (!userOpt.isPresent()) {
|
||||
return new RedirectView("/addUsers?messageType=userNotFound");
|
||||
}
|
||||
if (!userService.usernameExistsIgnoreCase(username)) {
|
||||
return new RedirectView("/addUsers?messageType=userNotFound");
|
||||
}
|
||||
// Get the currently authenticated username
|
||||
String currentUsername = authentication.getName();
|
||||
|
||||
// Check if the provided username matches the current session's username
|
||||
if (currentUsername.equalsIgnoreCase(username)) {
|
||||
return new RedirectView("/addUsers?messageType=downgradeCurrentUser");
|
||||
}
|
||||
try {
|
||||
// Validate the role
|
||||
Role roleEnum = Role.fromString(role);
|
||||
if (roleEnum == Role.INTERNAL_API_USER) {
|
||||
// If the role is INTERNAL_API_USER, reject the request
|
||||
return new RedirectView("/addUsers?messageType=invalidRole");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// If the role ID is not valid, redirect with an error message
|
||||
return new RedirectView("/addUsers?messageType=invalidRole");
|
||||
}
|
||||
User user = userOpt.get();
|
||||
|
||||
userService.changeRole(user, role);
|
||||
return new RedirectView("/addUsers"); // Redirect to account page after adding the user
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/admin/deleteUser/{username}")
|
||||
public RedirectView deleteUser(
|
||||
@PathVariable(name = "username") String username, Authentication authentication) {
|
||||
|
||||
if (!userService.usernameExists(username)) {
|
||||
if (!userService.usernameExistsIgnoreCase(username)) {
|
||||
return new RedirectView("/addUsers?messageType=deleteUsernameExists");
|
||||
}
|
||||
|
||||
@@ -239,7 +279,7 @@ public class UserController {
|
||||
String currentUsername = authentication.getName();
|
||||
|
||||
// Check if the provided username matches the current session's username
|
||||
if (currentUsername.equals(username)) {
|
||||
if (currentUsername.equalsIgnoreCase(username)) {
|
||||
return new RedirectView("/addUsers?messageType=deleteCurrentUser");
|
||||
}
|
||||
invalidateUserSessions(username);
|
||||
|
||||
@@ -6,10 +6,6 @@ import java.net.URLConnection;
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
@@ -39,7 +35,7 @@ public class ConvertImgPDFController {
|
||||
summary = "Convert PDF to image(s)",
|
||||
description =
|
||||
"This endpoint converts a PDF file to image(s) with the specified image format, color type, and DPI. Users can choose to get a single image or multiple images. Input:PDF Output:Image Type:SI-Conditional")
|
||||
public ResponseEntity<Resource> convertToImage(@ModelAttribute ConvertToImageRequest request)
|
||||
public ResponseEntity<byte[]> convertToImage(@ModelAttribute ConvertToImageRequest request)
|
||||
throws IOException {
|
||||
MultipartFile file = request.getFileInput();
|
||||
String imageFormat = request.getImageFormat();
|
||||
@@ -76,22 +72,15 @@ public class ConvertImgPDFController {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (singleImage) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
|
||||
ResponseEntity<Resource> response =
|
||||
new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK);
|
||||
return response;
|
||||
String docName = filename + "." + imageFormat;
|
||||
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));
|
||||
return WebResponseUtils.bytesToWebResponse(result, docName, mediaType);
|
||||
} else {
|
||||
ByteArrayResource resource = new ByteArrayResource(result);
|
||||
// return the Resource in the response
|
||||
return ResponseEntity.ok()
|
||||
.header(
|
||||
HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=" + filename + "_convertedToImages.zip")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.contentLength(resource.contentLength())
|
||||
.body(resource);
|
||||
String zipFilename = filename + "_convertedToImages.zip";
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
result, zipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.api.PDFFile;
|
||||
import stirling.software.SPDF.utils.PDFToFile;
|
||||
|
||||
@RestController
|
||||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
@RequestMapping("/api/v1/convert")
|
||||
public class ConvertPDFToHtml {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf/html")
|
||||
@Operation(
|
||||
summary = "Convert PDF to HTML",
|
||||
description =
|
||||
"This endpoint converts a PDF file to HTML format. Input:PDF Output:HTML Type:SISO")
|
||||
public ResponseEntity<byte[]> processPdfToHTML(@ModelAttribute PDFFile request)
|
||||
throws Exception {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToHtml(inputFile);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ 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.PDFFile;
|
||||
import stirling.software.SPDF.model.api.converters.PdfToPdfARequest;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
@@ -31,8 +31,10 @@ public class ConvertPDFToPDFA {
|
||||
summary = "Convert a PDF to a PDF/A",
|
||||
description =
|
||||
"This endpoint converts a PDF file to a PDF/A file. PDF/A is a format designed for long-term archiving of digital documents. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> pdfToPdfA(@ModelAttribute PDFFile request) throws Exception {
|
||||
public ResponseEntity<byte[]> pdfToPdfA(@ModelAttribute PdfToPdfARequest request)
|
||||
throws Exception {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
String outputFormat = request.getOutputFormat();
|
||||
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
@@ -47,7 +49,7 @@ public class ConvertPDFToPDFA {
|
||||
command.add("--skip-text");
|
||||
command.add("--tesseract-timeout=0");
|
||||
command.add("--output-type");
|
||||
command.add("pdfa");
|
||||
command.add(outputFormat.toString());
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
import java.awt.image.ConvolveOp;
|
||||
import java.awt.image.Kernel;
|
||||
import java.awt.image.RescaleOp;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
@@ -29,6 +31,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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;
|
||||
@@ -39,6 +42,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.api.PDFFile;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -48,98 +52,39 @@ public class FakeScanControllerWIP {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FakeScanControllerWIP.class);
|
||||
|
||||
// TODO
|
||||
// TODO finish
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/fake-scan")
|
||||
@Hidden
|
||||
// @PostMapping(consumes = "multipart/form-data", value = "/fakeScan")
|
||||
@Operation(
|
||||
summary = "Repair a PDF file",
|
||||
description =
|
||||
"This endpoint repairs a given PDF file by running Ghostscript command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response.")
|
||||
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request) throws IOException {
|
||||
public ResponseEntity<byte[]> fakeScan(@ModelAttribute PDFFile request) throws IOException {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
|
||||
// Load the PDF document
|
||||
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
pdfRenderer.setSubsamplingAllowed(true);
|
||||
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
|
||||
ImageIO.write(image, "png", new File("scanned-" + (page + 1) + ".png"));
|
||||
PDFRenderer renderer = new PDFRenderer(document);
|
||||
List<BufferedImage> images = new ArrayList<>();
|
||||
// Convert each page to an image
|
||||
for (int i = 0; i < document.getNumberOfPages(); i++) {
|
||||
BufferedImage image = renderer.renderImageWithDPI(i, 150, ImageType.GRAY);
|
||||
images.add(processImage(image));
|
||||
}
|
||||
document.close();
|
||||
|
||||
// Constants
|
||||
int scannedness = 90; // Value between 0 and 100
|
||||
int dirtiness = 0; // Value between 0 and 100
|
||||
|
||||
// Load the source image
|
||||
BufferedImage sourceImage = ImageIO.read(new File("scanned-1.png"));
|
||||
|
||||
// Create the destination image
|
||||
BufferedImage destinationImage =
|
||||
new BufferedImage(
|
||||
sourceImage.getWidth(), sourceImage.getHeight(), sourceImage.getType());
|
||||
|
||||
// Apply a brightness and contrast effect based on the "scanned-ness"
|
||||
float scaleFactor = 1.0f + (scannedness / 100.0f) * 0.5f; // Between 1.0 and 1.5
|
||||
float offset = scannedness * 1.5f; // Between 0 and 150
|
||||
BufferedImageOp op = new RescaleOp(scaleFactor, offset, null);
|
||||
op.filter(sourceImage, destinationImage);
|
||||
|
||||
// Apply a rotation effect
|
||||
double rotationRequired =
|
||||
Math.toRadians(
|
||||
(new SecureRandom().nextInt(3 - 1)
|
||||
+ 1)); // Random angle between 1 and 3 degrees
|
||||
double locationX = destinationImage.getWidth() / 2;
|
||||
double locationY = destinationImage.getHeight() / 2;
|
||||
AffineTransform tx =
|
||||
AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
|
||||
AffineTransformOp rotateOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
|
||||
destinationImage = rotateOp.filter(destinationImage, null);
|
||||
|
||||
// Apply a blur effect based on the "scanned-ness"
|
||||
float blurIntensity = scannedness / 100.0f * 0.2f; // Between 0.0 and 0.2
|
||||
float[] matrix = {
|
||||
blurIntensity, blurIntensity, blurIntensity,
|
||||
blurIntensity, blurIntensity, blurIntensity,
|
||||
blurIntensity, blurIntensity, blurIntensity
|
||||
};
|
||||
BufferedImageOp blurOp =
|
||||
new ConvolveOp(new Kernel(3, 3, matrix), ConvolveOp.EDGE_NO_OP, null);
|
||||
destinationImage = blurOp.filter(destinationImage, null);
|
||||
|
||||
// Add noise to the image based on the "dirtiness"
|
||||
Random random = new SecureRandom();
|
||||
for (int y = 0; y < destinationImage.getHeight(); y++) {
|
||||
for (int x = 0; x < destinationImage.getWidth(); x++) {
|
||||
if (random.nextInt(100) < dirtiness) {
|
||||
// Change the pixel color to black randomly based on the "dirtiness"
|
||||
destinationImage.setRGB(x, y, Color.BLACK.getRGB());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the image
|
||||
ImageIO.write(destinationImage, "PNG", new File("scanned-1.png"));
|
||||
|
||||
PDDocument documentOut = new PDDocument();
|
||||
for (int page = 1; page <= document.getNumberOfPages(); ++page) {
|
||||
BufferedImage bim = ImageIO.read(new File("scanned-" + page + ".png"));
|
||||
|
||||
// Adjust the dimensions of the page
|
||||
PDPage pdPage = new PDPage(new PDRectangle(bim.getWidth() - 1, bim.getHeight() - 1));
|
||||
documentOut.addPage(pdPage);
|
||||
|
||||
PDImageXObject pdImage = LosslessFactory.createFromImage(documentOut, bim);
|
||||
PDPageContentStream contentStream = new PDPageContentStream(documentOut, pdPage);
|
||||
|
||||
// Draw the image with a slight offset and enlarged dimensions
|
||||
contentStream.drawImage(pdImage, -1, -1, bim.getWidth() + 2, bim.getHeight() + 2);
|
||||
contentStream.close();
|
||||
}
|
||||
// Create a new PDF document with the processed images
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
documentOut.save(baos);
|
||||
documentOut.close();
|
||||
PDDocument newDocument = new PDDocument();
|
||||
for (BufferedImage img : images) {
|
||||
// PDPageContentStream contentStream = new PDPageContentStream(newDocument, new
|
||||
// PDPage());
|
||||
PDImageXObject pdImage = JPEGFactory.createFromImage(newDocument, img);
|
||||
PdfUtils.addImageToDocument(newDocument, pdImage, "maintainAspectRatio", false);
|
||||
}
|
||||
|
||||
newDocument.save(baos);
|
||||
newDocument.close();
|
||||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename =
|
||||
@@ -148,4 +93,232 @@ public class FakeScanControllerWIP {
|
||||
+ "_scanned.pdf";
|
||||
return WebResponseUtils.boasToWebResponse(baos, outputFilename);
|
||||
}
|
||||
|
||||
public BufferedImage processImage(BufferedImage image) {
|
||||
// Rotation
|
||||
|
||||
image = softenEdges(image, 50);
|
||||
image = rotate(image, 1);
|
||||
|
||||
image = applyGaussianBlur(image, 0.5);
|
||||
addGaussianNoise(image, 0.5);
|
||||
image = linearStretch(image);
|
||||
addDustAndHairs(image, 3);
|
||||
return image;
|
||||
}
|
||||
|
||||
private BufferedImage rotate(BufferedImage image, double rotation) {
|
||||
|
||||
double rotationRequired = Math.toRadians(rotation);
|
||||
double locationX = image.getWidth() / 2;
|
||||
double locationY = image.getHeight() / 2;
|
||||
AffineTransform tx =
|
||||
AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
|
||||
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC);
|
||||
return op.filter(image, null);
|
||||
}
|
||||
|
||||
private BufferedImage applyGaussianBlur(BufferedImage image, double sigma) {
|
||||
int radius = 3; // Fixed radius size for simplicity
|
||||
|
||||
int size = 2 * radius + 1;
|
||||
float[] data = new float[size * size];
|
||||
double sum = 0.0;
|
||||
|
||||
for (int i = -radius; i <= radius; i++) {
|
||||
for (int j = -radius; j <= radius; j++) {
|
||||
double xDistance = i * i;
|
||||
double yDistance = j * j;
|
||||
double g = Math.exp(-(xDistance + yDistance) / (2 * sigma * sigma));
|
||||
data[(i + radius) * size + j + radius] = (float) g;
|
||||
sum += g;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the kernel
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] /= sum;
|
||||
}
|
||||
|
||||
Kernel kernel = new Kernel(size, size, data);
|
||||
BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
|
||||
return op.filter(image, null);
|
||||
}
|
||||
|
||||
public BufferedImage softenEdges(BufferedImage image, int featherRadius) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
Graphics2D g2 = output.createGraphics();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2.setRenderingHint(
|
||||
RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
|
||||
g2.drawImage(image, 0, 0, null);
|
||||
g2.setComposite(AlphaComposite.DstIn);
|
||||
|
||||
// Top edge
|
||||
g2.setPaint(
|
||||
new GradientPaint(
|
||||
0,
|
||||
0,
|
||||
new Color(0, 0, 0, 1f),
|
||||
0,
|
||||
featherRadius * 2,
|
||||
new Color(0, 0, 0, 0f)));
|
||||
g2.fillRect(0, 0, width, featherRadius);
|
||||
|
||||
// Bottom edge
|
||||
g2.setPaint(
|
||||
new GradientPaint(
|
||||
0,
|
||||
height - featherRadius * 2,
|
||||
new Color(0, 0, 0, 0f),
|
||||
0,
|
||||
height,
|
||||
new Color(0, 0, 0, 1f)));
|
||||
g2.fillRect(0, height - featherRadius, width, featherRadius);
|
||||
|
||||
// Left edge
|
||||
g2.setPaint(
|
||||
new GradientPaint(
|
||||
0,
|
||||
0,
|
||||
new Color(0, 0, 0, 1f),
|
||||
featherRadius * 2,
|
||||
0,
|
||||
new Color(0, 0, 0, 0f)));
|
||||
g2.fillRect(0, 0, featherRadius, height);
|
||||
|
||||
// Right edge
|
||||
g2.setPaint(
|
||||
new GradientPaint(
|
||||
width - featherRadius * 2,
|
||||
0,
|
||||
new Color(0, 0, 0, 0f),
|
||||
width,
|
||||
0,
|
||||
new Color(0, 0, 0, 1f)));
|
||||
g2.fillRect(width - featherRadius, 0, featherRadius, height);
|
||||
|
||||
g2.dispose();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private void addDustAndHairs(BufferedImage image, float intensity) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
Graphics2D g2d = image.createGraphics();
|
||||
Random random = new SecureRandom();
|
||||
|
||||
// Set rendering hints for better quality
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// Calculate the number of artifacts based on intensity
|
||||
int numSpots = (int) (intensity * 10);
|
||||
int numHairs = (int) (intensity * 20);
|
||||
|
||||
// Add spots with more variable sizes
|
||||
g2d.setColor(new Color(100, 100, 100, 50)); // Semi-transparent gray
|
||||
for (int i = 0; i < numSpots; i++) {
|
||||
int x = random.nextInt(width);
|
||||
int y = random.nextInt(height);
|
||||
int ovalSize = 1 + random.nextInt(3); // Base size + variable component
|
||||
if (random.nextFloat() > 0.9) {
|
||||
// 10% chance to get a larger spot
|
||||
ovalSize += random.nextInt(3);
|
||||
}
|
||||
g2d.fill(new Ellipse2D.Double(x, y, ovalSize, ovalSize));
|
||||
}
|
||||
|
||||
// Add hairs
|
||||
g2d.setStroke(new BasicStroke(0.5f)); // Thin stroke for hairs
|
||||
g2d.setColor(new Color(80, 80, 80, 40)); // Slightly lighter and more transparent
|
||||
for (int i = 0; i < numHairs; i++) {
|
||||
int x1 = random.nextInt(width);
|
||||
int y1 = random.nextInt(height);
|
||||
int x2 = x1 + random.nextInt(20) - 10; // Random length and direction
|
||||
int y2 = y1 + random.nextInt(20) - 10;
|
||||
Path2D.Double hair = new Path2D.Double();
|
||||
hair.moveTo(x1, y1);
|
||||
hair.curveTo(x1, y1, (x1 + x2) / 2, (y1 + y2) / 2, x2, y2);
|
||||
g2d.draw(hair);
|
||||
}
|
||||
|
||||
g2d.dispose();
|
||||
}
|
||||
|
||||
private void addGaussianNoise(BufferedImage image, double strength) {
|
||||
Random rand = new SecureRandom();
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
int rgba = image.getRGB(i, j);
|
||||
int alpha = (rgba >> 24) & 0xff;
|
||||
int red = (rgba >> 16) & 0xff;
|
||||
int green = (rgba >> 8) & 0xff;
|
||||
int blue = rgba & 0xff;
|
||||
|
||||
// Apply Gaussian noise
|
||||
red = (int) (red + rand.nextGaussian() * strength);
|
||||
green = (int) (green + rand.nextGaussian() * strength);
|
||||
blue = (int) (blue + rand.nextGaussian() * strength);
|
||||
|
||||
// Clamping values to the 0-255 range
|
||||
red = Math.min(Math.max(0, red), 255);
|
||||
green = Math.min(Math.max(0, green), 255);
|
||||
blue = Math.min(Math.max(0, blue), 255);
|
||||
|
||||
image.setRGB(i, j, (alpha << 24) | (red << 16) | (green << 8) | blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BufferedImage linearStretch(BufferedImage image) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
int min = 255;
|
||||
int max = 0;
|
||||
|
||||
// First pass: find the min and max grayscale values
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int rgb = image.getRGB(x, y);
|
||||
int gray =
|
||||
(int)
|
||||
(((rgb >> 16) & 0xff) * 0.299
|
||||
+ ((rgb >> 8) & 0xff) * 0.587
|
||||
+ (rgb & 0xff) * 0.114); // Convert to grayscale
|
||||
if (gray < min) min = gray;
|
||||
if (gray > max) max = gray;
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: stretch the histogram
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int rgb = image.getRGB(x, y);
|
||||
int alpha = (rgb >> 24) & 0xff;
|
||||
int red = (rgb >> 16) & 0xff;
|
||||
int green = (rgb >> 8) & 0xff;
|
||||
int blue = rgb & 0xff;
|
||||
|
||||
// Apply linear stretch to each channel
|
||||
red = (int) (((red - min) / (float) (max - min)) * 255);
|
||||
green = (int) (((green - min) / (float) (max - min)) * 255);
|
||||
blue = (int) (((blue - min) / (float) (max - min)) * 255);
|
||||
|
||||
// Set new RGB value maintaining the alpha channel
|
||||
rgb = (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
image.setRGB(x, y, rgb);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.print.PageFormat;
|
||||
import java.awt.print.Printable;
|
||||
import java.awt.print.PrinterException;
|
||||
import java.awt.print.PrinterJob;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.print.PrintService;
|
||||
import javax.print.PrintServiceLookup;
|
||||
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.printing.PDFPageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.PrintFileRequest;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/misc")
|
||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||
public class PrintFileController {
|
||||
|
||||
// TODO
|
||||
// @PostMapping(value = "/print-file", consumes = "multipart/form-data")
|
||||
// @Operation(
|
||||
// summary = "Prints PDF/Image file to a set printer",
|
||||
// description =
|
||||
// "Input of PDF or Image along with a printer name/URL/IP to match against to
|
||||
// send it to (Fire and forget) Input:Any Output:N/A Type:SISO")
|
||||
public ResponseEntity<String> printFile(@ModelAttribute PrintFileRequest request)
|
||||
throws IOException {
|
||||
MultipartFile file = request.getFileInput();
|
||||
String printerName = request.getPrinterName();
|
||||
String contentType = file.getContentType();
|
||||
try {
|
||||
// Find matching printer
|
||||
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
|
||||
PrintService selectedService =
|
||||
Arrays.stream(services)
|
||||
.filter(
|
||||
service ->
|
||||
service.getName().toLowerCase().contains(printerName))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
"No matching printer found"));
|
||||
|
||||
System.out.println("Selected Printer: " + selectedService.getName());
|
||||
|
||||
if ("application/pdf".equals(contentType)) {
|
||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||
PrinterJob job = PrinterJob.getPrinterJob();
|
||||
job.setPrintService(selectedService);
|
||||
job.setPageable(new PDFPageable(document));
|
||||
job.print();
|
||||
document.close();
|
||||
} else if (contentType.startsWith("image/")) {
|
||||
BufferedImage image = ImageIO.read(file.getInputStream());
|
||||
PrinterJob job = PrinterJob.getPrinterJob();
|
||||
job.setPrintService(selectedService);
|
||||
job.setPrintable(
|
||||
new Printable() {
|
||||
public int print(
|
||||
Graphics graphics, PageFormat pageFormat, int pageIndex)
|
||||
throws PrinterException {
|
||||
if (pageIndex != 0) {
|
||||
return NO_SUCH_PAGE;
|
||||
}
|
||||
Graphics2D g2d = (Graphics2D) graphics;
|
||||
g2d.translate(
|
||||
pageFormat.getImageableX(), pageFormat.getImageableY());
|
||||
g2d.drawImage(
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
(int) pageFormat.getImageableWidth(),
|
||||
(int) pageFormat.getImageableHeight(),
|
||||
null);
|
||||
return PAGE_EXISTS;
|
||||
}
|
||||
});
|
||||
job.print();
|
||||
}
|
||||
return new ResponseEntity<>(
|
||||
"File printed successfully to " + selectedService.getName(), HttpStatus.OK);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to print: " + e.getMessage());
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,25 +139,29 @@ public class SanitizeController {
|
||||
|
||||
for (PDPage page : allPages) {
|
||||
PDResources res = page.getResources();
|
||||
|
||||
// Remove embedded files from the PDF
|
||||
res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles"));
|
||||
if (res != null && res.getCOSObject() != null) {
|
||||
res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sanitizeMetadata(PDDocument document) {
|
||||
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
|
||||
if (metadata != null) {
|
||||
document.getDocumentCatalog().setMetadata(null);
|
||||
if (document.getDocumentCatalog() != null) {
|
||||
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
|
||||
if (metadata != null) {
|
||||
document.getDocumentCatalog().setMetadata(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sanitizeLinks(PDDocument document) throws IOException {
|
||||
for (PDPage page : document.getPages()) {
|
||||
for (PDAnnotation annotation : page.getAnnotations()) {
|
||||
if (annotation instanceof PDAnnotationLink) {
|
||||
if (annotation != null && annotation instanceof PDAnnotationLink) {
|
||||
PDAction action = ((PDAnnotationLink) annotation).getAction();
|
||||
if (action instanceof PDActionLaunch || action instanceof PDActionURI) {
|
||||
if (action != null
|
||||
&& (action instanceof PDActionLaunch
|
||||
|| action instanceof PDActionURI)) {
|
||||
((PDAnnotationLink) annotation).setAction(null);
|
||||
}
|
||||
}
|
||||
@@ -167,7 +171,11 @@ public class SanitizeController {
|
||||
|
||||
private void sanitizeFonts(PDDocument document) {
|
||||
for (PDPage page : document.getPages()) {
|
||||
page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font"));
|
||||
if (page != null
|
||||
&& page.getResources() != null
|
||||
&& page.getResources().getCOSObject() != null) {
|
||||
page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -19,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.Authority;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import stirling.software.SPDF.model.User;
|
||||
@@ -28,12 +30,17 @@ import stirling.software.SPDF.repository.UserRepository;
|
||||
@Tag(name = "Account Security", description = "Account Security APIs")
|
||||
public class AccountWebController {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
@GetMapping("/login")
|
||||
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
model.addAttribute(
|
||||
"oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled());
|
||||
|
||||
model.addAttribute("currentPage", "login");
|
||||
|
||||
if (request.getParameter("error") != null) {
|
||||
@@ -85,17 +92,32 @@ public class AccountWebController {
|
||||
}
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
Object principal = authentication.getPrincipal();
|
||||
String username = null;
|
||||
|
||||
if (principal instanceof UserDetails) {
|
||||
// Cast the principal object to UserDetails
|
||||
UserDetails userDetails = (UserDetails) principal;
|
||||
|
||||
// Retrieve username and other attributes
|
||||
String username = userDetails.getUsername();
|
||||
username = userDetails.getUsername();
|
||||
|
||||
// Add oAuth2 Login attributes to the model
|
||||
model.addAttribute("oAuth2Login", false);
|
||||
}
|
||||
if (principal instanceof OAuth2User) {
|
||||
// Cast the principal object to OAuth2User
|
||||
OAuth2User userDetails = (OAuth2User) principal;
|
||||
|
||||
// Retrieve username and other attributes
|
||||
username = userDetails.getAttribute("email");
|
||||
|
||||
// Add oAuth2 Login attributes to the model
|
||||
model.addAttribute("oAuth2Login", true);
|
||||
}
|
||||
if (username != null) {
|
||||
// Fetch user details from the database
|
||||
Optional<User> user =
|
||||
userRepository.findByUsername(
|
||||
userRepository.findByUsernameIgnoreCase(
|
||||
username); // Assuming findByUsername method exists
|
||||
if (!user.isPresent()) {
|
||||
// Handle error appropriately
|
||||
@@ -145,7 +167,7 @@ public class AccountWebController {
|
||||
|
||||
// Fetch user details from the database
|
||||
Optional<User> user =
|
||||
userRepository.findByUsername(
|
||||
userRepository.findByUsernameIgnoreCase(
|
||||
username); // Assuming findByUsername method exists
|
||||
if (!user.isPresent()) {
|
||||
// Handle error appropriately
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@Controller
|
||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||
public class OtherWebController {
|
||||
|
||||
@GetMapping("/compress-pdf")
|
||||
@Hidden
|
||||
public String compressPdfForm(Model model) {
|
||||
@@ -53,6 +54,13 @@ public class OtherWebController {
|
||||
return "misc/add-page-numbers";
|
||||
}
|
||||
|
||||
@GetMapping("/fake-scan")
|
||||
@Hidden
|
||||
public String fakeScanForm(Model model) {
|
||||
model.addAttribute("currentPage", "fake-scan");
|
||||
return "misc/fake-scan";
|
||||
}
|
||||
|
||||
@GetMapping("/extract-images")
|
||||
@Hidden
|
||||
public String extractImagesForm(Model model) {
|
||||
@@ -81,6 +89,13 @@ public class OtherWebController {
|
||||
return "misc/compare";
|
||||
}
|
||||
|
||||
@GetMapping("/print-file")
|
||||
@Hidden
|
||||
public String printFileForm(Model model) {
|
||||
model.addAttribute("currentPage", "print-file");
|
||||
return "misc/print-file";
|
||||
}
|
||||
|
||||
public List<String> getAvailableTesseractLanguages() {
|
||||
String tessdataDir = "/usr/share/tessdata";
|
||||
File[] files = new File(tessdataDir).listFiles();
|
||||
|
||||
@@ -118,6 +118,7 @@ public class ApplicationProperties {
|
||||
private Boolean enableLogin;
|
||||
private Boolean csrfDisabled;
|
||||
private InitialLogin initialLogin;
|
||||
private OAUTH2 oauth2;
|
||||
private int loginAttemptCount;
|
||||
private long loginResetTimeMinutes;
|
||||
|
||||
@@ -145,6 +146,14 @@ public class ApplicationProperties {
|
||||
this.initialLogin = initialLogin;
|
||||
}
|
||||
|
||||
public OAUTH2 getOAUTH2() {
|
||||
return oauth2 != null ? oauth2 : new OAUTH2();
|
||||
}
|
||||
|
||||
public void setOAUTH2(OAUTH2 oauth2) {
|
||||
this.oauth2 = oauth2;
|
||||
}
|
||||
|
||||
public Boolean getEnableLogin() {
|
||||
return enableLogin;
|
||||
}
|
||||
@@ -165,6 +174,8 @@ public class ApplicationProperties {
|
||||
public String toString() {
|
||||
return "Security [enableLogin="
|
||||
+ enableLogin
|
||||
+ ", oauth2="
|
||||
+ oauth2
|
||||
+ ", initialLogin="
|
||||
+ initialLogin
|
||||
+ ", csrfDisabled="
|
||||
@@ -202,6 +213,70 @@ public class ApplicationProperties {
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
public static class OAUTH2 {
|
||||
|
||||
private boolean enabled;
|
||||
private String issuer;
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private boolean autoCreateUser;
|
||||
|
||||
public boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public void setIssuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public boolean getAutoCreateUser() {
|
||||
return autoCreateUser;
|
||||
}
|
||||
|
||||
public void setAutoCreateUser(boolean autoCreateUser) {
|
||||
this.autoCreateUser = autoCreateUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OAUTH2 [enabled="
|
||||
+ enabled
|
||||
+ ", issuer="
|
||||
+ issuer
|
||||
+ ", clientId="
|
||||
+ clientId
|
||||
+ ", clientSecret="
|
||||
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
|
||||
+ ", autoCreateUser="
|
||||
+ autoCreateUser
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class System {
|
||||
@@ -210,6 +285,33 @@ public class ApplicationProperties {
|
||||
private String rootURIPath;
|
||||
private String customStaticFilePath;
|
||||
private Integer maxFileSize;
|
||||
private boolean showUpdate;
|
||||
private Boolean showUpdateOnlyAdmin;
|
||||
private boolean customHTMLFiles;
|
||||
|
||||
public boolean isCustomHTMLFiles() {
|
||||
return customHTMLFiles;
|
||||
}
|
||||
|
||||
public void setCustomHTMLFiles(boolean customHTMLFiles) {
|
||||
this.customHTMLFiles = customHTMLFiles;
|
||||
}
|
||||
|
||||
public boolean getShowUpdateOnlyAdmin() {
|
||||
return showUpdateOnlyAdmin;
|
||||
}
|
||||
|
||||
public void setShowUpdateOnlyAdmin(boolean showUpdateOnlyAdmin) {
|
||||
this.showUpdateOnlyAdmin = showUpdateOnlyAdmin;
|
||||
}
|
||||
|
||||
public boolean getShowUpdate() {
|
||||
return showUpdate;
|
||||
}
|
||||
|
||||
public void setShowUpdate(boolean showUpdate) {
|
||||
this.showUpdate = showUpdate;
|
||||
}
|
||||
|
||||
private Boolean enableAlphaFunctionality;
|
||||
|
||||
@@ -275,6 +377,10 @@ public class ApplicationProperties {
|
||||
+ maxFileSize
|
||||
+ ", enableAlphaFunctionality="
|
||||
+ enableAlphaFunctionality
|
||||
+ ", showUpdate="
|
||||
+ showUpdate
|
||||
+ ", showUpdateOnlyAdmin="
|
||||
+ showUpdateOnlyAdmin
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
19
src/main/java/stirling/software/SPDF/model/PdfMetadata.java
Normal file
19
src/main/java/stirling/software/SPDF/model/PdfMetadata.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package stirling.software.SPDF.model;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class PdfMetadata {
|
||||
private String author;
|
||||
private String producer;
|
||||
private String title;
|
||||
private String creator;
|
||||
private String subject;
|
||||
private String keywords;
|
||||
private Calendar creationDate;
|
||||
private Calendar modificationDate;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package stirling.software.SPDF.model.api.converters;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import stirling.software.SPDF.model.api.PDFFile;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PdfToPdfARequest extends PDFFile {
|
||||
|
||||
@Schema(
|
||||
description = "The output PDF/A type",
|
||||
allowableValues = {"pdfa", "pdfa-1"})
|
||||
private String outputFormat;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package stirling.software.SPDF.model.api.misc;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import stirling.software.SPDF.model.api.PDFFile;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PrintFileRequest extends PDFFile {
|
||||
|
||||
@Schema(description = "Name of printer to match against", required = true)
|
||||
private String printerName;
|
||||
}
|
||||
@@ -9,4 +9,6 @@ import stirling.software.SPDF.model.Authority;
|
||||
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
|
||||
// Set<Authority> findByUsername(String username);
|
||||
Set<Authority> findByUser_Username(String username);
|
||||
|
||||
Authority findByUserId(long user_id);
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ public class GeneralUtils {
|
||||
}
|
||||
|
||||
sizeStr = sizeStr.trim().toUpperCase();
|
||||
sizeStr = sizeStr.replace(",", ".").replace(" ", "");
|
||||
try {
|
||||
if (sizeStr.endsWith("KB")) {
|
||||
return (long)
|
||||
@@ -185,7 +186,7 @@ public class GeneralUtils {
|
||||
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
|
||||
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
|
||||
// Now replace 'n' with its current value
|
||||
return withMultiplication.replaceAll("n", String.valueOf(nValue));
|
||||
return withMultiplication.replace("n", String.valueOf(nValue));
|
||||
}
|
||||
|
||||
private static List<Integer> handlePart(String part, int totalPages, int offset) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
@@ -19,11 +20,8 @@ import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.*;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
|
||||
import org.apache.pdfbox.pdmodel.PDResources;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||
@@ -39,6 +37,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
|
||||
import stirling.software.SPDF.model.PdfMetadata;
|
||||
|
||||
public class PdfUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
||||
@@ -336,14 +336,12 @@ public class PdfUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addImageToDocument(
|
||||
public static void addImageToDocument(
|
||||
PDDocument doc, PDImageXObject image, String fitOption, boolean autoRotate)
|
||||
throws IOException {
|
||||
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
||||
PDRectangle pageSize = PDRectangle.A4;
|
||||
|
||||
System.out.println(fitOption);
|
||||
|
||||
if (autoRotate && imageIsLandscape) {
|
||||
pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth());
|
||||
}
|
||||
@@ -421,4 +419,28 @@ public class PdfUtils {
|
||||
logger.info("PDF successfully saved to byte array");
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public static PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
|
||||
return PdfMetadata.builder()
|
||||
.author(pdf.getDocumentInformation().getAuthor())
|
||||
.producer(pdf.getDocumentInformation().getProducer())
|
||||
.title(pdf.getDocumentInformation().getTitle())
|
||||
.creator(pdf.getDocumentInformation().getCreator())
|
||||
.subject(pdf.getDocumentInformation().getSubject())
|
||||
.keywords(pdf.getDocumentInformation().getKeywords())
|
||||
.creationDate(pdf.getDocumentInformation().getCreationDate())
|
||||
.modificationDate(pdf.getDocumentInformation().getModificationDate())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata) {
|
||||
pdf.getDocumentInformation().setAuthor(pdfMetadata.getAuthor());
|
||||
pdf.getDocumentInformation().setProducer(pdfMetadata.getProducer());
|
||||
pdf.getDocumentInformation().setTitle(pdfMetadata.getTitle());
|
||||
pdf.getDocumentInformation().setCreator(pdfMetadata.getCreator());
|
||||
pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject());
|
||||
pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords());
|
||||
pdf.getDocumentInformation().setCreationDate(pdfMetadata.getCreationDate());
|
||||
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
|
||||
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -116,6 +118,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=الإعدادات
|
||||
settings.update=التحديث متاح
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=إصدار التطبيق:
|
||||
settings.downloadOption.title=تحديد خيار التنزيل (للتنزيلات ذات الملف الواحد غير المضغوط):
|
||||
settings.downloadOption.1=فتح في نفس النافذة
|
||||
@@ -124,8 +127,9 @@ settings.downloadOption.3=تنزيل الملف
|
||||
settings.zipThreshold=ملفات مضغوطة عند تجاوز عدد الملفات التي تم تنزيلها
|
||||
settings.signOut=Sign Out
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -175,6 +179,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=تغيير دور المستخدم
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -439,6 +444,8 @@ login.rememberme=Remember me
|
||||
login.invalid=Invalid username or password.
|
||||
login.locked=Your account has been locked.
|
||||
login.signinTitle=Please sign in
|
||||
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
|
||||
login.oauth2AutoCreateDisabled=تم تعطيل مستخدم الإنشاء التلقائي لـ OAuth2
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -944,6 +951,7 @@ pdfToPDFA.header=PDF إلى PDF / A
|
||||
pdfToPDFA.credit=تستخدم هذه الخدمة OCRmyPDF لتحويل PDF / A.
|
||||
pdfToPDFA.submit=تحويل
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1027,6 +1035,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Новият потребител вече съществ
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
|
||||
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Настройки
|
||||
settings.update=Налична актуализация
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Версия на приложението:
|
||||
settings.downloadOption.title=Изберете опция за изтегляне (за изтегляния на един файл без да е архивиран):
|
||||
settings.downloadOption.1=Отваряне в същия прозорец
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Изтегли файл
|
||||
settings.zipThreshold=Архивирайте файловете, когато броят на изтеглените файлове надвишава
|
||||
settings.signOut=Изход
|
||||
settings.accountSettings=Настройки на акаунта
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Промяна на идентификационните данни
|
||||
changeCreds.header=Актуализирайте данните за акаунта си
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
|
||||
adminUserSettings.submit=Съхранете потребителя
|
||||
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Запомни ме
|
||||
login.invalid=Невалидно потребителско име или парола.
|
||||
login.locked=Вашият акаунт е заключен.
|
||||
login.signinTitle=Моля впишете се
|
||||
login.ssoSignIn=Влизане чрез еднократно влизане
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF към PDF/A
|
||||
pdfToPDFA.credit=Тази услуга използва OCRmyPDF за PDF/A преобразуване.
|
||||
pdfToPDFA.submit=Преобразуване
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual
|
||||
downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Opcions
|
||||
settings.update=Actualització Disponible
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Versió App:
|
||||
settings.downloadOption.title=Trieu l'opció de descàrrega (per a descàrregues d'un sol fitxer no zip):
|
||||
settings.downloadOption.1=Obre mateixa finestra
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Descarrega Arxiu
|
||||
settings.zipThreshold=Comprimiu els fitxers quan el nombre de fitxers baixats superi
|
||||
settings.signOut=Sortir
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Desar Usuari
|
||||
adminUserSettings.changeUserRole=Canvia el rol de l'usuari
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Recordar
|
||||
login.invalid=Nom usuari / password no vàlid
|
||||
login.locked=Compte bloquejat
|
||||
login.signinTitle=Autenticat
|
||||
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
|
||||
login.oauth2AutoCreateDisabled=L'usuari de creació automàtica OAUTH2 està desactivat
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF a PDF/A
|
||||
pdfToPDFA.credit=Utilitza OCRmyPDF per la conversió a PDF/A
|
||||
pdfToPDFA.submit=Converteix
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -46,7 +46,7 @@ green=Grün
|
||||
blue=Blau
|
||||
custom=benutzerdefiniert...
|
||||
WorkInProgess=In Arbeit, funktioniert möglicherweise nicht oder ist fehlerhaft. Bitte melden Sie alle Probleme!
|
||||
poweredBy=Powered by
|
||||
poweredBy=Unterstützt von
|
||||
yes=Ja
|
||||
no=Nein
|
||||
changedCredsMessage=Anmeldedaten geändert!
|
||||
@@ -57,15 +57,17 @@ usernameExistsMessage=Neuer Benutzername existiert bereits.
|
||||
invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben und Zahlen enthalten.
|
||||
deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden.
|
||||
deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
goHomepage=Go to Homepage
|
||||
joinDiscord=Join our Discord server
|
||||
seeDockerHub=See Docker Hub
|
||||
visitGithub=Visit Github Repository
|
||||
donate=Donate
|
||||
color=Color
|
||||
downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden
|
||||
downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt.
|
||||
error=Fehler
|
||||
oops=Hoppla!
|
||||
help=Hilfe
|
||||
goHomepage=Zur Startseite gehen
|
||||
joinDiscord=Unserem Discord-Server beitreten
|
||||
seeDockerHub=Docker Hub ansehen
|
||||
visitGithub=GitHub-Repository besuchen
|
||||
donate=Spenden
|
||||
color=Farbe
|
||||
sponsor=Sponsor
|
||||
info=Info
|
||||
|
||||
@@ -79,8 +81,8 @@ pipeline.uploadButton=Benutzerdefinierter Upload
|
||||
pipeline.configureButton=Konfigurieren
|
||||
pipeline.defaultOption=Benutzerdefiniert
|
||||
pipeline.submitButton=Speichern
|
||||
pipeline.help=Pipeline Help
|
||||
pipeline.scanHelp=Folder Scanning Help
|
||||
pipeline.help=Hilfe für Pipeline
|
||||
pipeline.scanHelp=Hilfe zum Ordnerscan
|
||||
|
||||
######################
|
||||
# Pipeline Options #
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Einstellungen
|
||||
settings.update=Update verfügbar
|
||||
settings.updateAvailable={0} ist die aktuelle installierte Version. Eine neue Version ({1}) ist verfügbar.
|
||||
settings.appVersion=App-Version:
|
||||
settings.downloadOption.title=Download-Option wählen (für einzelne Dateien, die keine Zip-Downloads sind):
|
||||
settings.downloadOption.1=Im selben Fenster öffnen
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Datei herunterladen
|
||||
settings.zipThreshold=Dateien komprimieren, wenn die Anzahl der heruntergeladenen Dateien überschritten wird
|
||||
settings.signOut=Abmelden
|
||||
settings.accountSettings=Kontoeinstellungen
|
||||
|
||||
|
||||
settings.bored.help=Aktiviert das Easter-Egg-Spiel
|
||||
settings.cacheInputs.name=Formulareingaben speichern
|
||||
settings.cacheInputs.help=Aktivieren, um zuvor verwendete Eingaben für zukünftige Durchläufe zu speichern
|
||||
|
||||
changeCreds.title=Anmeldeinformationen ändern
|
||||
changeCreds.header=Aktualisieren Sie Ihre Kontodaten
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo-Benutzer (Keine benutzerdefinierten Einstellunge
|
||||
adminUserSettings.internalApiUser=Interner API-Benutzer
|
||||
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
||||
adminUserSettings.submit=Benutzer speichern
|
||||
adminUserSettings.changeUserRole=Benutzerrolle ändern
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -193,58 +198,58 @@ home.multiTool.desc=Seiten zusammenführen, drehen, neu anordnen und entfernen
|
||||
multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side
|
||||
|
||||
home.merge.title=Zusammenführen
|
||||
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen.
|
||||
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen
|
||||
merge.tags=zusammenführen,seitenvorgänge,back end,serverseite
|
||||
|
||||
home.split.title=Aufteilen
|
||||
home.split.desc=PDFs in mehrere Dokumente aufteilen.
|
||||
home.split.desc=PDFs in mehrere Dokumente aufteilen
|
||||
split.tags=seitenoperationen,teilen,mehrseitig,ausschneiden,serverseitig
|
||||
|
||||
home.rotate.title=Drehen
|
||||
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach.
|
||||
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach
|
||||
rotate.tags=serverseitig
|
||||
|
||||
|
||||
home.imageToPdf.title=Bild zu PDF
|
||||
home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF.
|
||||
home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF
|
||||
imageToPdf.tags=konvertierung,img,jpg,bild,foto
|
||||
|
||||
home.pdfToImage.title=PDF zu Bild
|
||||
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF).
|
||||
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF)
|
||||
pdfToImage.tags=konvertierung,img,jpg,bild,foto
|
||||
|
||||
home.pdfOrganiser.title=Organisieren
|
||||
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern.
|
||||
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern
|
||||
pdfOrganiser.tags=duplex,gerade,ungerade,sortieren,verschieben
|
||||
|
||||
|
||||
home.addImage.title=Bild einfügen
|
||||
home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit).
|
||||
home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit)
|
||||
addImage.tags=img,jpg,bild,foto
|
||||
|
||||
home.watermark.title=Wasserzeichen hinzufügen
|
||||
home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu.
|
||||
home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu
|
||||
watermark.tags=text,wiederholend,beschriftung,besitzen,urheberrecht,marke,img,jpg,bild,foto
|
||||
|
||||
home.permissions.title=Berechtigungen ändern
|
||||
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern.
|
||||
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern
|
||||
permissions.tags=lesen,schreiben,bearbeiten,drucken
|
||||
|
||||
|
||||
home.removePages.title=Entfernen
|
||||
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen.
|
||||
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen
|
||||
removePages.tags=seiten entfernen,seiten löschen
|
||||
|
||||
home.addPassword.title=Passwort hinzufügen
|
||||
home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln.
|
||||
home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln
|
||||
addPassword.tags=sicher,sicherheit
|
||||
|
||||
home.removePassword.title=Passwort entfernen
|
||||
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen.
|
||||
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen
|
||||
removePassword.tags=sichern,entschlüsseln,sicherheit,passwort aufheben,passwort löschen
|
||||
|
||||
home.compressPdfs.title=Komprimieren
|
||||
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
|
||||
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren
|
||||
compressPdfs.tags=komprimieren,verkleinern,minimieren
|
||||
|
||||
|
||||
@@ -257,7 +262,7 @@ home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, P
|
||||
fileToPDF.tags=transformation,format,dokument,bild,folie,text,konvertierung,büro,dokumente,word,excel,powerpoint
|
||||
|
||||
home.ocr.title=Führe OCR/Cleanup-Scans aus
|
||||
home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu.
|
||||
home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu
|
||||
ocr.tags=erkennung,text,bild,scannen,lesen,identifizieren,erkennung,bearbeitbar
|
||||
|
||||
|
||||
@@ -379,17 +384,17 @@ getPdfInfo.tags=infomation,daten,statistik
|
||||
|
||||
home.extractPage.title=Seite(n) extrahieren
|
||||
home.extractPage.desc=Extrahiert ausgewählte Seiten aus einer PDF
|
||||
extractPage.tags=extrahieren
|
||||
extractPage.tags=extrahieren,seite
|
||||
|
||||
|
||||
home.PdfToSinglePage.title=PDF zu einer Seite zusammenfassen
|
||||
home.PdfToSinglePage.desc=Fügt alle PDF-Seiten zu einer einzigen großen Seite zusammen
|
||||
PdfToSinglePage.tags=einzelseite
|
||||
PdfToSinglePage.tags=einzelseite,zusammenfassen
|
||||
|
||||
|
||||
home.showJS.title=Javascript anzeigen
|
||||
home.showJS.desc=Alle Javascript Funktionen in einer PDF anzeigen
|
||||
showJS.tags=js
|
||||
showJS.tags=js,javascript
|
||||
|
||||
home.autoRedact.title=Automatisch zensieren/schwärzen
|
||||
home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text
|
||||
@@ -397,7 +402,7 @@ autoRedact.tags=zensieren,schwärzen
|
||||
|
||||
home.tableExtraxt.title=Tabelle extrahieren
|
||||
home.tableExtraxt.desc=Tabelle aus PDF in CSV extrahieren
|
||||
tableExtraxt.tags=CSV
|
||||
tableExtraxt.tags=CSV,tabelle,extrahieren
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Teilen nach Größe/Anzahl
|
||||
@@ -420,11 +425,11 @@ AddStampRequest.tags=stempeln,bild hinzufügen,bild zentrieren,wasserzeichen,pdf
|
||||
|
||||
home.PDFToBook.title=PDF zum Buch
|
||||
home.PDFToBook.desc=Konvertiert PDF mit Calibre in Buch-/Comic-Formate
|
||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
||||
PDFToBook.tags=buch,comic,calibre,convert,manga,amazon,kindle
|
||||
|
||||
home.BookToPDF.title=Buch als PDF
|
||||
home.BookToPDF.desc=Konvertiert Buch-/Comic-Formate mithilfe von Calibre in PDF
|
||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
||||
BookToPDF.tags=buch,comic,calibre,convert,manga,amazon,kindle
|
||||
|
||||
|
||||
###########################
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Angemeldet bleiben
|
||||
login.invalid=Benutzername oder Passwort ungültig.
|
||||
login.locked=Ihr Konto wurde gesperrt.
|
||||
login.signinTitle=Bitte melden Sie sich an.
|
||||
login.ssoSignIn=Anmeldung per Single Sign-On
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -450,7 +457,7 @@ autoRedact.textsToRedactLabel=Zu zensierender Text (einer pro Zeile)
|
||||
autoRedact.textsToRedactPlaceholder=z.B. \nVertraulich \nStreng geheim
|
||||
autoRedact.useRegexLabel=Regex verwenden
|
||||
autoRedact.wholeWordSearchLabel=Ganzes Wort suchen
|
||||
autoRedact.customPaddingLabel=Benutzerdefinierte Extra-Padding
|
||||
autoRedact.customPaddingLabel=Zensierten Bereich vergrößern
|
||||
autoRedact.convertPDFToImageLabel=PDF in PDF-Bild konvertieren (zum Entfernen von Text hinter dem Kasten)
|
||||
autoRedact.submitButton=Zensieren
|
||||
|
||||
@@ -472,7 +479,7 @@ pdfToSinglePage.submit=Zusammenfassen
|
||||
pageExtracter.title=Seiten extrahieren
|
||||
pageExtracter.header=Seiten extrahieren
|
||||
pageExtracter.submit=Extrahieren
|
||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||
pageExtracter.placeholder=(z.B. 1,2,8 oder 4,7,12-16 oder 2n-1)
|
||||
|
||||
|
||||
#getPdfInfo
|
||||
@@ -504,16 +511,16 @@ HTMLToPDF.header=HTML zu PDF
|
||||
HTMLToPDF.help=Akzeptiert HTML-Dateien und ZIPs mit html/css/images etc.
|
||||
HTMLToPDF.submit=Konvertieren
|
||||
HTMLToPDF.credit=Verwendet WeasyPrint
|
||||
HTMLToPDF.zoom=Zoomstufe zur Darstellung der Website.
|
||||
HTMLToPDF.pageWidth=Breite der Seite in Zentimetern. (Leer auf Standard)
|
||||
HTMLToPDF.pageHeight=Höhe der Seite in Zentimetern. (Leer auf Standard)
|
||||
HTMLToPDF.marginTop=Oberer Rand der Seite in Millimetern. (Leer auf Standard)
|
||||
HTMLToPDF.marginBottom=Unterer Rand der Seite in Millimetern. (Leer auf Standard)
|
||||
HTMLToPDF.marginLeft=Linker Rand der Seite in Millimetern. (Leer auf Standard)
|
||||
HTMLToPDF.marginRight=Linker Rand der Seite in Millimetern. (Leer auf Standard)
|
||||
HTMLToPDF.printBackground=Den Hintergrund der Website rendern.
|
||||
HTMLToPDF.zoom=Zoomstufe zur Darstellung der Website
|
||||
HTMLToPDF.pageWidth=Breite der Seite in Zentimetern (Leer auf Standard)
|
||||
HTMLToPDF.pageHeight=Höhe der Seite in Zentimetern (Leer auf Standard)
|
||||
HTMLToPDF.marginTop=Oberer Rand der Seite in Millimetern (Leer auf Standard)
|
||||
HTMLToPDF.marginBottom=Unterer Rand der Seite in Millimetern (Leer auf Standard)
|
||||
HTMLToPDF.marginLeft=Linker Rand der Seite in Millimetern (Leer auf Standard)
|
||||
HTMLToPDF.marginRight=Linker Rand der Seite in Millimetern (Leer auf Standard)
|
||||
HTMLToPDF.printBackground=Den Hintergrund der Website rendern
|
||||
HTMLToPDF.defaultHeader=Standardkopfzeile aktivieren (Name und Seitenzahl)
|
||||
HTMLToPDF.cssMediaType=CSS-Medientyp der Seite ändern.
|
||||
HTMLToPDF.cssMediaType=CSS-Medientyp der Seite ändern
|
||||
HTMLToPDF.none=Keine
|
||||
HTMLToPDF.print=Drucken
|
||||
HTMLToPDF.screen=Bildschirm
|
||||
@@ -614,8 +621,8 @@ pageLayout.submit=Abschicken
|
||||
#scalePages
|
||||
scalePages.title=Seitengröße anpassen
|
||||
scalePages.header=Seitengröße anpassen
|
||||
scalePages.pageSize=Format der Seiten des Dokuments.
|
||||
scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite.
|
||||
scalePages.pageSize=Format der Seiten des Dokuments
|
||||
scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite
|
||||
scalePages.submit=Abschicken
|
||||
|
||||
|
||||
@@ -759,7 +766,7 @@ compress.submit=Komprimieren
|
||||
#Add image
|
||||
addImage.title=Bild hinzufügen
|
||||
addImage.header=Ein Bild einfügen
|
||||
addImage.everyPage=Jede Seite?
|
||||
addImage.everyPage=In jede Seite einfügen?
|
||||
addImage.upload=Bild hinzufügen
|
||||
addImage.submit=Bild hinzufügen
|
||||
|
||||
@@ -776,23 +783,23 @@ merge.submit=Zusammenführen
|
||||
pdfOrganiser.title=Seiten anordnen
|
||||
pdfOrganiser.header=PDF Seitenorganisation
|
||||
pdfOrganiser.submit=Seiten anordnen
|
||||
pdfOrganiser.mode=Mode
|
||||
pdfOrganiser.mode.1=Custom Page Order
|
||||
pdfOrganiser.mode.2=Reverse Order
|
||||
pdfOrganiser.mode.3=Duplex Sort
|
||||
pdfOrganiser.mode.4=Booklet Sort
|
||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
||||
pdfOrganiser.mode.6=Odd-Even Split
|
||||
pdfOrganiser.mode.7=Remove First
|
||||
pdfOrganiser.mode.8=Remove Last
|
||||
pdfOrganiser.mode.9=Remove First and Last
|
||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||
pdfOrganiser.mode=Modus
|
||||
pdfOrganiser.mode.1=Benutzerdefinierte Seitenreihenfolge
|
||||
pdfOrganiser.mode.2=Umgekehrte Reihenfolge
|
||||
pdfOrganiser.mode.3=Duplex-Sortierung
|
||||
pdfOrganiser.mode.4=Heftsortierung
|
||||
pdfOrganiser.mode.5=Seitenheftungs-Heftsortierung
|
||||
pdfOrganiser.mode.6=Ungerade-Gerade-Teilung
|
||||
pdfOrganiser.mode.7=Erste entfernen
|
||||
pdfOrganiser.mode.8=Letzte entfernen
|
||||
pdfOrganiser.mode.9=Erste und letzte entfernen
|
||||
pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)
|
||||
|
||||
|
||||
#multiTool
|
||||
multiTool.title=PDF-Multitool
|
||||
multiTool.header=PDF-Multitool
|
||||
multiTool.uploadPrompts=Please Upload PDF
|
||||
multiTool.uploadPrompts=Bitte PDF hochladen
|
||||
|
||||
#view pdf
|
||||
viewPdf.title=PDF anzeigen
|
||||
@@ -803,7 +810,7 @@ pageRemover.title=Seiten entfernen
|
||||
pageRemover.header=PDF Seiten entfernen
|
||||
pageRemover.pagesToDelete=Seiten zu entfernen (geben Sie eine Kommagetrennte Liste der Seitenzahlen an):
|
||||
pageRemover.submit=Seiten löschen
|
||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
||||
pageRemover.placeholder=(z.B. 1,2,6 oder 1-10,15-30)
|
||||
|
||||
|
||||
#rotate
|
||||
@@ -892,7 +899,7 @@ watermark.selectText.8=Wasserzeichen Typ:
|
||||
watermark.selectText.9=Wasserzeichen-Bild:
|
||||
watermark.submit=Wasserzeichen hinzufügen
|
||||
watermark.type.1=Text
|
||||
watermark.type.2=Image
|
||||
watermark.type.2=Bild
|
||||
|
||||
|
||||
#Change permissions
|
||||
@@ -944,7 +951,8 @@ pdfToPDFA.title=PDF zu PDF/A
|
||||
pdfToPDFA.header=PDF zu PDF/A
|
||||
pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF für die PDF/A-Konvertierung
|
||||
pdfToPDFA.submit=Konvertieren
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten.
|
||||
pdfToPDFA.outputFormat=Ausgabeformat
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Anzahl vertikaler Teiler eingeben
|
||||
split-by-sections.submit=PDF teilen
|
||||
split-by-sections.merge=In eine PDF zusammenfügen
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Datei drucken
|
||||
printFile.header=Datei an Drucker senden
|
||||
printFile.selectText.1=Wähle die auszudruckende Datei
|
||||
printFile.selectText.2=Druckernamen eingeben
|
||||
printFile.submit=Drucken
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Lizenzen
|
||||
licenses.title=Lizenzen von Drittanbietern
|
||||
@@ -1038,15 +1055,15 @@ licenses.license=Lizenz
|
||||
|
||||
|
||||
# error
|
||||
error.sorry=Sorry for the issue!
|
||||
error.needHelp=Need help / Found an issue?
|
||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
||||
error.404.1=We can't seem to find the page you're looking for.
|
||||
error.404.2=Something went wrong
|
||||
error.github=Submit a ticket on GitHub
|
||||
error.showStack=Show Stack Trace
|
||||
error.copyStack=Copy Stack Trace
|
||||
error.githubSubmit=GitHub - Submit a ticket
|
||||
error.discordSubmit=Discord - Submit Support post
|
||||
error.sorry=Entschuldigung für das Problem!
|
||||
error.needHelp=Brauchst du Hilfe / Ein Problem gefunden?
|
||||
error.contactTip=Wenn du weiterhin Probleme hast, zögere nicht, uns um Hilfe zu bitten. Du kannst ein Ticket auf unserer GitHub-Seite einreichen oder uns über Discord kontaktieren:
|
||||
error.404.head=404 - Seite nicht gefunden | Ups, wir sind im Code gestolpert!
|
||||
error.404.1=Wir können die gesuchte Seite nicht finden.
|
||||
error.404.2=Etwas ist schiefgelaufen
|
||||
error.github=Ein Ticket auf GitHub einreichen
|
||||
error.showStack=Stack-Trace anzeigen
|
||||
error.copyStack=Stack-Trace kopieren
|
||||
error.githubSubmit=GitHub - Ein Ticket einreichen
|
||||
error.discordSubmit=Discord - Unterstützungsbeitrag einreichen
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@ multiPdfDropPrompt=Επιλογή (ή τράβηγμα αρχείου και α
|
||||
imgPrompt=Επιλογή Εικόνας(Εικόνων)
|
||||
genericSubmit=Υποβολή
|
||||
processTimeWarning=Προσοχή: Αυτή η διαδικασία μπορεί να διαρκέσει έως και ένα λεπτό ανάλογα με το μέγεθος του αρχείου
|
||||
pageOrderPrompt=Προσαρμοσμένη Σειρά Σελίδας (Προσθέστε μία λίστε απο αριθμούς σελιδών, χωρισμένες με κόμμα ή συναρτήσεις όπως 2n+1) :
|
||||
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
|
||||
goToPage=Go
|
||||
pageOrderPrompt=Προσαρμοσμένη Σειρά Σελίδας (Προσθέστε μία λίστα απο αριθμούς σελιδών, χωρισμένες με κόμμα ή συναρτήσεις όπως 2n+1) :
|
||||
pageSelectionPrompt=Προσαρμοσμένη Επιλογή Σελίδας (Προσθέστε μία λίστα απο αριθμούς σελιδών, χωρισμένες με κόμμα 1,5,6 ή συναρτήσεις όπως 2n+1):
|
||||
goToPage=Πήγαινε
|
||||
true=Αληθές
|
||||
false=Λανθασμένο
|
||||
unknown=Άγνωστο
|
||||
save=Αποθήκευση
|
||||
saveToBrowser=Save to Browser
|
||||
saveToBrowser=Αποθήκευση στο Browser
|
||||
close=Κλείσιμο
|
||||
filesSelected=αρχεία που επιλέχθηκαν
|
||||
noFavourites=Κανένα αγαπήμενο δεν έχει προστεθεί
|
||||
downloadComplete=Download Complete
|
||||
downloadComplete=Η Λήψη Ολοκληρώθηκε
|
||||
bored=Βαριέστε να περιμένετε;
|
||||
alphabet=Αλφάβητο
|
||||
downloadPdf=Κατέβασμα του PDF
|
||||
@@ -45,28 +45,30 @@ red=Κόκκινο
|
||||
green=Πράσινο
|
||||
blue=Μπλέ
|
||||
custom=Προσαρμογή...
|
||||
WorkInProgess=Work in progress, May not work or be buggy, Please report any problems!
|
||||
poweredBy=Powered by
|
||||
yes=Yes
|
||||
no=No
|
||||
WorkInProgess=Εργασία σε εξέλιξη, Ενδέχεται να μην λειτουργεί ή να έχει λάθη, Παρακαλώ αναφέρετε τυχόν προβλήματα!
|
||||
poweredBy=Τροφοδοτείται από
|
||||
yes=Ναι
|
||||
no=Όχι
|
||||
changedCredsMessage=Τα διαπιστευτήρια έχουν αλλάξει!
|
||||
notAuthenticatedMessage=Ο χρήστης δεν έχει αυθεντικοποιηθεί.
|
||||
userNotFoundMessage=Ο χρήστης δεν βρέθηκε.
|
||||
incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος.
|
||||
usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
goHomepage=Go to Homepage
|
||||
joinDiscord=Join our Discord server
|
||||
seeDockerHub=See Docker Hub
|
||||
visitGithub=Visit Github Repository
|
||||
donate=Donate
|
||||
color=Color
|
||||
sponsor=Sponsor
|
||||
invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, το όνομα χρήστη πρέπει να περιέχει μόνο αλφαβητικούς χαρακτήρες και αριθμούς.
|
||||
deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη.
|
||||
deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί.
|
||||
downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη
|
||||
downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται.
|
||||
error=Σφάλμα
|
||||
oops=Ωχ!
|
||||
help=Βοήθεια
|
||||
goHomepage=Πήγαινε στην Αρχική Σελίδα
|
||||
joinDiscord=Εγγραφείτε στο Server του Discord μας
|
||||
seeDockerHub=Βλέπε το Docker Hub
|
||||
visitGithub=Επισκεφθείτε το Αποθετήριο του Github
|
||||
donate=Δωρισε
|
||||
color=Χρώμα
|
||||
sponsor=Yποστηρικτής
|
||||
info=Info
|
||||
|
||||
|
||||
@@ -74,26 +76,26 @@ info=Info
|
||||
###############
|
||||
# Pipeline #
|
||||
###############
|
||||
pipeline.header=Pipeline Menu (Beta)
|
||||
pipeline.header=Μενού Pipeline (Beta)
|
||||
pipeline.uploadButton=Upload Custom
|
||||
pipeline.configureButton=Configure
|
||||
pipeline.configureButton=Διαμόρφωσε
|
||||
pipeline.defaultOption=Custom
|
||||
pipeline.submitButton=Submit
|
||||
pipeline.help=Pipeline Help
|
||||
pipeline.scanHelp=Folder Scanning Help
|
||||
pipeline.submitButton=Υποβολή
|
||||
pipeline.help=Βοήθεια για το Pipeline
|
||||
pipeline.scanHelp=Βοήθεια για Σάρωση Φακέλων
|
||||
|
||||
######################
|
||||
# Pipeline Options #
|
||||
######################
|
||||
pipelineOptions.header=Pipeline Configuration
|
||||
pipelineOptions.pipelineNameLabel=Pipeline Name
|
||||
pipelineOptions.saveSettings=Save Operation Settings
|
||||
pipelineOptions.pipelineNamePrompt=Enter pipeline name here
|
||||
pipelineOptions.selectOperation=Select Operation
|
||||
pipelineOptions.addOperationButton=Add operation
|
||||
pipelineOptions.header=Διαμόρφωση Pipeline
|
||||
pipelineOptions.pipelineNameLabel=Όνομα Pipeline
|
||||
pipelineOptions.saveSettings=Αποθήκευση Ρυθμίσεων Λειτουργίας
|
||||
pipelineOptions.pipelineNamePrompt=Εισαγάγετε το όνομα του pipeline εδώ
|
||||
pipelineOptions.selectOperation=Επιλέξτε Λειτουργία
|
||||
pipelineOptions.addOperationButton=Προσθήκη λειτουργίας
|
||||
pipelineOptions.pipelineHeader=Pipeline:
|
||||
pipelineOptions.saveButton=Download
|
||||
pipelineOptions.validateButton=Validate
|
||||
pipelineOptions.saveButton=Λήψη
|
||||
pipelineOptions.validateButton=Επικυρώνω
|
||||
|
||||
|
||||
|
||||
@@ -117,20 +119,22 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Ρυθμίσεις
|
||||
settings.update=Υπάρχει διαθέσιμη ενημέρωση
|
||||
settings.appVersion=Έκδοση εφαρμογής: App Version:
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Έκδοση εφαρμογής:
|
||||
settings.downloadOption.title=Επιλέξετε την επιλογή λήψης (Για λήψεις μεμονωμένων αρχείων χωρίς zip):
|
||||
settings.downloadOption.1=Άνοιγμα στο ίδιο παράθυρο
|
||||
settings.downloadOption.2=Άνοιγμα σε νέο παράθυρο
|
||||
settings.downloadOption.3=Λήψη αρχείου
|
||||
settings.zipThreshold=Zip αρχεία όταν ο αριθμός των ληφθέντων αρχείων είναι πολύ μεγάλος
|
||||
settings.zipThreshold=Αρχεία Zip όταν ο αριθμός των ληφθέντων αρχείων είναι πολύ μεγάλος
|
||||
settings.signOut=Αποσύνδεση
|
||||
settings.accountSettings=Ρυθμίσεις Λογαριασμού
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Αλλαγή Διαπιστευτηρίων
|
||||
changeCreds.header=Ενημέρωση των λεπτομερειών του Λογαριασμού σας
|
||||
changeCreds.changePassword=You are using default login credentials. Please enter a new password
|
||||
changeCreds.changePassword=Χρησιμοποιείτε προεπιλεγμένα διαπιστευτήρια σύνδεσης. Εισαγάγετε έναν νέο κωδικό πρόσβασης
|
||||
changeCreds.newUsername=Νέο Όνομα Χρήστη
|
||||
changeCreds.oldPassword=Τρέχων Κωδικός Πρόσβασης
|
||||
changeCreds.newPassword=Νέος Κωδικός Πρόσβασης
|
||||
@@ -170,18 +174,19 @@ adminUserSettings.roles=Ρόλοι
|
||||
adminUserSettings.role=Ρόλος
|
||||
adminUserSettings.actions=Ενέργειες
|
||||
adminUserSettings.apiUser=Περιορισμένος Χρήστης για διεπαφή προγραμματισμού εφαρμογών (API User)
|
||||
adminUserSettings.extraApiUser=Additional Limited API User
|
||||
adminUserSettings.extraApiUser=Πρόσθετος Περιορισμένος Χρήστης για Διεπαφή Προγραμματισμού Εφαρμογών (API User)
|
||||
adminUserSettings.webOnlyUser=Χρήστης μόνο Ιστού
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.demoUser=Demo χρήστης (Χωρίς προσαρμοσμένες ρυθμίσεις)
|
||||
adminUserSettings.internalApiUser=Εσωτερικός API χρήστης
|
||||
adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλάξει το όνομα χρήστη/κωδικό πρόσβασης κατά τη σύνδεση
|
||||
adminUserSettings.submit=Αποθήκευση Χρήστη
|
||||
adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=Η τοπικά φιλοξενούμενη one-stop-shop σας για όλες τις ανάγκες σας σε PDF.
|
||||
home.searchBar=Search for features...
|
||||
home.searchBar=Αναζήτηση για χαρακτηριστικά...
|
||||
|
||||
|
||||
home.viewPdf.title=Εμφάνιση PDF
|
||||
@@ -202,7 +207,7 @@ split.tags=Page operations,divide,Multi Page,cut,server side
|
||||
|
||||
home.rotate.title=Περιστροφή
|
||||
home.rotate.desc=Περιστροφή των PDF σας με εύκολο τρόπο.
|
||||
rotate.tags=server side
|
||||
rotate.tags=από την πλευρά του server
|
||||
|
||||
|
||||
home.imageToPdf.title=Εικόνα σε PDF
|
||||
@@ -395,35 +400,35 @@ home.autoRedact.title=Αυτόματο Μαύρισμα Κειμένου
|
||||
home.autoRedact.desc=Αυτόματη επεξεργασία (Μαύρισμα) κείμενου σε PDF με βάση το κείμενο εισαγωγής
|
||||
autoRedact.tags=Redact,Hide,black out,black,marker,hidden
|
||||
|
||||
home.tableExtraxt.title=PDF to CSV
|
||||
home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV
|
||||
home.tableExtraxt.title=PDF σε CSV
|
||||
home.tableExtraxt.desc=Εξάγει πίνακες από PDF μετατρέποντάς το σε CSV
|
||||
tableExtraxt.tags=CSV,Table Extraction,extract,convert
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Auto Split by Size/Count
|
||||
home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count
|
||||
home.autoSizeSplitPDF.title=Αυτόματη διαίρεση κατά μέγεθος/πλήθος
|
||||
home.autoSizeSplitPDF.desc=Διαχωρίστε ένα μόνο PDF σε πολλά έγγραφα με βάση το μέγεθος, τον αριθμό σελίδων ή τον αριθμό εγγράφων
|
||||
autoSizeSplitPDF.tags=pdf,split,document,organization
|
||||
|
||||
|
||||
home.overlay-pdfs.title=Overlay PDFs
|
||||
home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF
|
||||
home.overlay-pdfs.title=Επικάλυψη PDFs
|
||||
home.overlay-pdfs.desc=Επικαλύπτει αρχεία PDF πάνω σε άλλο PDF
|
||||
overlay-pdfs.tags=Overlay
|
||||
|
||||
home.split-by-sections.title=Split PDF by Sections
|
||||
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
|
||||
home.split-by-sections.title=Διαχωρισμός PDF ανά ενότητες
|
||||
home.split-by-sections.desc=Διαχωρίστε κάθε σελίδα ενός PDF σε μικρότερες οριζόντιες και κάθετες ενότητες
|
||||
split-by-sections.tags=Section Split, Divide, Customize
|
||||
|
||||
home.AddStampRequest.title=Add Stamp to PDF
|
||||
home.AddStampRequest.desc=Add text or add image stamps at set locations
|
||||
home.AddStampRequest.title=Προσθήκη σφραγίδας σε PDF
|
||||
home.AddStampRequest.desc=Προσθέστε κείμενο ή προσθέστε σφραγίδες εικόνας σε καθορισμένες τοποθεσίες
|
||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||
|
||||
|
||||
home.PDFToBook.title=PDF to Book
|
||||
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
|
||||
home.PDFToBook.title=PDF σε Book
|
||||
home.PDFToBook.desc=Μετατρέπει τις μορφές PDF σε Book/Comic χρησιμοποιώντας calibre
|
||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
||||
|
||||
home.BookToPDF.title=Book to PDF
|
||||
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
|
||||
home.BookToPDF.title=Book σε PDF
|
||||
home.BookToPDF.desc=Μετατρέπει τις μορφές Books/Comics σε PDF χρησιμοποιώντας calibre
|
||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
||||
|
||||
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Να Με Θυμάσαι
|
||||
login.invalid=Λάθος όνομα χρήστη ή κωδικού πρόσβασης.
|
||||
login.locked=Ο λογαριασμός σας έχει κλειδωθεί.
|
||||
login.signinTitle=Παρακαλώ, συνδεθείτε
|
||||
login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης
|
||||
login.oauth2AutoCreateDisabled=Απενεργοποιήθηκε ο χρήστης αυτόματης δημιουργίας OAUTH2
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -472,7 +479,7 @@ pdfToSinglePage.submit=Μετατροπή σε Μονή Σελίδα
|
||||
pageExtracter.title=Εξαγωγή Σελίδων
|
||||
pageExtracter.header=EΕξαγωγή Σελίδων
|
||||
pageExtracter.submit=Εξαγωγή
|
||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||
pageExtracter.placeholder=(π.χ. 1,2,8 ή 4,7,12-16 ή 2n-1)
|
||||
|
||||
|
||||
#getPdfInfo
|
||||
@@ -504,37 +511,37 @@ HTMLToPDF.header=HTML σε PDF
|
||||
HTMLToPDF.help=Δέχεται αρχεία τύπου HTML και τύπου ZIP που περιέχουν html/css/εικόνες κ.λπ. που απαιτούνται
|
||||
HTMLToPDF.submit=Μετατροπή
|
||||
HTMLToPDF.credit=Χρησιμοποιεί WeasyPrint
|
||||
HTMLToPDF.zoom=Zoom level for displaying the website.
|
||||
HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default)
|
||||
HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default)
|
||||
HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.printBackground=Render the background of websites.
|
||||
HTMLToPDF.defaultHeader=Enable Default Header (Name and page number)
|
||||
HTMLToPDF.cssMediaType=Change the CSS media type of the page.
|
||||
HTMLToPDF.none=None
|
||||
HTMLToPDF.print=Print
|
||||
HTMLToPDF.zoom=Επίπεδο ζουμ για την εμφάνιση του ιστότοπου.
|
||||
HTMLToPDF.pageWidth=Το πλάτος της σελίδας σε εκατοστά. (Κενό έως προεπιλογή)
|
||||
HTMLToPDF.pageHeight=Το ύψος της σελίδας σε εκατοστά. (Κενό έως προεπιλογή)
|
||||
HTMLToPDF.marginTop=Το επάνω περιθώριο της σελίδας σε χιλιοστά. (Κενό έως προεπιλογή)
|
||||
HTMLToPDF.marginBottom=Κάτω περιθώριο σελίδας σε χιλιοστά. (Κενό έως προεπιλογή)
|
||||
HTMLToPDF.marginLeft=Το αριστερό περιθώριο της σελίδας σε χιλιοστά. (Κενό έως προεπιλογή)
|
||||
HTMLToPDF.marginRight=Το δεξί περιθώριο της σελίδας σε χιλιοστά. (Κενό έως προεπιλογή)
|
||||
HTMLToPDF.printBackground=Αποδώστε το φόντο των ιστοσελίδων.
|
||||
HTMLToPDF.defaultHeader=Ενεργοποίηση προεπιλεγμένης κεφαλίδας (Όνομα και αριθμός σελίδας)
|
||||
HTMLToPDF.cssMediaType=Αλλάξτε τον τύπο μέσων (media type )CSS της σελίδας.
|
||||
HTMLToPDF.none=Κανένα
|
||||
HTMLToPDF.print=Εκτύπωσε
|
||||
HTMLToPDF.screen=Screen
|
||||
|
||||
|
||||
#AddStampRequest
|
||||
AddStampRequest.header=Stamp PDF
|
||||
AddStampRequest.title=Stamp PDF
|
||||
AddStampRequest.stampType=Stamp Type
|
||||
AddStampRequest.stampText=Stamp Text
|
||||
AddStampRequest.stampImage=Stamp Image
|
||||
AddStampRequest.alphabet=Alphabet
|
||||
AddStampRequest.fontSize=Font/Image Size
|
||||
AddStampRequest.rotation=Rotation
|
||||
AddStampRequest.opacity=Opacity
|
||||
AddStampRequest.position=Position
|
||||
AddStampRequest.overrideX=Override X Coordinate
|
||||
AddStampRequest.overrideY=Override Y Coordinate
|
||||
AddStampRequest.customMargin=Custom Margin
|
||||
AddStampRequest.customColor=Custom Text Color
|
||||
AddStampRequest.submit=Submit
|
||||
AddStampRequest.header=Σφραγίδα PDF
|
||||
AddStampRequest.title=Σφραγίδα PDF
|
||||
AddStampRequest.stampType=Τύπος σφραγίδας
|
||||
AddStampRequest.stampText=Κείμενο σφραγίδας
|
||||
AddStampRequest.stampImage=Εικόνα σφραγίδας
|
||||
AddStampRequest.alphabet=Αλφάβητο
|
||||
AddStampRequest.fontSize=Μέγεθος γραμματοσειράς/εικόνας
|
||||
AddStampRequest.rotation=Περιστροφή
|
||||
AddStampRequest.opacity=Αδιαφάνεια
|
||||
AddStampRequest.position=Θέση
|
||||
AddStampRequest.overrideX=Override Συντεταγμένης X
|
||||
AddStampRequest.overrideY=Override Συντεταγμένης Ψ
|
||||
AddStampRequest.customMargin=Προσαρμοσμένο Περιθώριο
|
||||
AddStampRequest.customColor=Προσαρμοσμένο χρώμα κειμένου
|
||||
AddStampRequest.submit=Υποβολή
|
||||
|
||||
|
||||
#sanitizePDF
|
||||
@@ -623,11 +630,11 @@ scalePages.submit=Υποβολή
|
||||
certSign.title=Υπογραφή Πιστοποιητικού
|
||||
certSign.header=Υπογράψτε ένα αρχείο PDF με το πιστοποιητικό σας (Εργασία σε εξέλιξη)
|
||||
certSign.selectPDF=Επιλογή αρχείου PDF για υπογραφή:
|
||||
certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below.
|
||||
certSign.jksNote=Σημείωση: Εάν ο τύπος πιστοποιητικού σας δεν αναφέρεται παρακάτω, μετατρέψτε το σε αρχείο Java Keystore (.jks) χρησιμοποιώντας το εργαλείο γραμμής εντολών keytool. Στη συνέχεια, επιλέξτε την επιλογή αρχείου .jks παρακάτω.
|
||||
certSign.selectKey=Επιλέξτε το αρχείο του ιδιωτικού κλειδιού σας (μορφή PKCS#8, μπορεί να είναι .pem ή .der):
|
||||
certSign.selectCert=Επιλέξτε το αρχείο πιστοποιητικού σας (μορφή X.509, μπορεί να είναι .pem ή .der):
|
||||
certSign.selectP12=Επιλέξτε το αρχείο PKCS#12 Keystore (.p12 ή .pfx) (Προαιρετικό, εάν παρέχεται, θα πρέπει να περιέχει το ιδιωτικό κλειδί και το πιστοποιητικό σας):
|
||||
certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore):
|
||||
certSign.selectJKS=Επιλέξτε το αρχείο Java Keystore (.jks ή .keystore):
|
||||
certSign.certType=Τύπος Πιστοποιητικού
|
||||
certSign.password=Εισαγάγετε τον κωδικό του Keystore ή του Ιδιωτικού Κλειδιού (εάν υπάρχει):
|
||||
certSign.showSig=Εμφάνιση Υπογραφής
|
||||
@@ -648,9 +655,9 @@ removeBlanks.submit=Αφαίρεση Κενών
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
removeAnnotations.title=Κατάργηση σχολιασμών
|
||||
removeAnnotations.header=Κατάργηση σχολιασμών
|
||||
removeAnnotations.submit=Κατάργηση
|
||||
|
||||
|
||||
#compare
|
||||
@@ -661,17 +668,17 @@ compare.document.2=Έγγραφο 2
|
||||
compare.submit=Σύγκριση
|
||||
|
||||
#BookToPDF
|
||||
BookToPDF.title=Books and Comics to PDF
|
||||
BookToPDF.header=Book to PDF
|
||||
BookToPDF.credit=Uses Calibre
|
||||
BookToPDF.submit=Convert
|
||||
BookToPDF.title=Books και Comics σε PDF
|
||||
BookToPDF.header=Book σε PDF
|
||||
BookToPDF.credit=Χρησιμοποιεί Caliber
|
||||
BookToPDF.submit=Μετατροπή
|
||||
|
||||
#PDFToBook
|
||||
PDFToBook.title=PDF to Book
|
||||
PDFToBook.header=PDF to Book
|
||||
PDFToBook.selectText.1=Format
|
||||
PDFToBook.credit=Uses Calibre
|
||||
PDFToBook.submit=Convert
|
||||
PDFToBook.title=PDF σε Book
|
||||
PDFToBook.header=PDF σε Book
|
||||
PDFToBook.selectText.1=Μορφή
|
||||
PDFToBook.credit=Χρησιμοποιεί Caliber
|
||||
PDFToBook.submit=Μετατροπή
|
||||
|
||||
#sign
|
||||
sign.title=Υπογραφή
|
||||
@@ -759,7 +766,7 @@ compress.submit=Συμπίεση
|
||||
#Add image
|
||||
addImage.title=Προσθήκη Εικόνας
|
||||
addImage.header=Προσθήκη Εικόνας σε PDF
|
||||
addImage.everyPage=Κάθε Σελίδα?
|
||||
addImage.everyPage=Κάθε Σελίδα;
|
||||
addImage.upload=Προσθήκη Εικόνας
|
||||
addImage.submit=Προσθήκη Εικόνας
|
||||
|
||||
@@ -777,40 +784,40 @@ pdfOrganiser.title=Διοργανωτής σελίδας
|
||||
pdfOrganiser.header=Διοργανωτής σελίδας PDF
|
||||
pdfOrganiser.submit=Αναδιάταξη σελίδων
|
||||
pdfOrganiser.mode=Mode
|
||||
pdfOrganiser.mode.1=Custom Page Order
|
||||
pdfOrganiser.mode.2=Reverse Order
|
||||
pdfOrganiser.mode.3=Duplex Sort
|
||||
pdfOrganiser.mode.4=Booklet Sort
|
||||
pdfOrganiser.mode.1=Προσαρμοσμένη Σειρά Σελίδας
|
||||
pdfOrganiser.mode.2=Αντίστροφη Σειρά
|
||||
pdfOrganiser.mode.3=Ταξινόμηση Διπλής Όψης (Duplex Sort)
|
||||
pdfOrganiser.mode.4=Ταξινόμηση Φυλλαδίου (Booklet Sort)
|
||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
||||
pdfOrganiser.mode.6=Odd-Even Split
|
||||
pdfOrganiser.mode.7=Remove First
|
||||
pdfOrganiser.mode.8=Remove Last
|
||||
pdfOrganiser.mode.9=Remove First and Last
|
||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||
pdfOrganiser.mode.6=Διαίρεση Μονού-Ζυγού
|
||||
pdfOrganiser.mode.7=Αφαίρεση Πρώτου
|
||||
pdfOrganiser.mode.8=Αφαίρεση Τελευταίου
|
||||
pdfOrganiser.mode.9=Αφαίρεση Πρώτου και Τελευταίου
|
||||
pdfOrganiser.placeholder=(π.χ. 1,3,2 ή 4-8,2,10-12 ή 2n-1)
|
||||
|
||||
|
||||
#multiTool
|
||||
multiTool.title=PDF Πολυεργαλείο
|
||||
multiTool.header=PDF Πολυεργαλείο
|
||||
multiTool.uploadPrompts=Please Upload PDF
|
||||
multiTool.uploadPrompts=Ανεβάστε το PDF
|
||||
|
||||
#view pdf
|
||||
viewPdf.title=View PDF
|
||||
viewPdf.header=View PDF
|
||||
viewPdf.title=Προβολή PDF
|
||||
viewPdf.header=Προβολή PDF
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Αφαιρετής Σελίδων
|
||||
pageRemover.header=Αφαιρετής Σελίδων PDF
|
||||
pageRemover.pagesToDelete=Σελίδες προς διαγραφή (Εισαγάγετε μια λίστα με αριθμούς σελίδων διαχωρισμένων με κόμματα):
|
||||
pageRemover.submit=Αφαίρεση Σελίδων
|
||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
||||
pageRemover.placeholder=(π.χ. 1,2,6 ή 1-10,15-30)
|
||||
|
||||
|
||||
#rotate
|
||||
rotate.title=Περιστροφή PDF
|
||||
rotate.header=Περιστροφή PDF
|
||||
rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
|
||||
rotate.submit=Rotate
|
||||
rotate.selectAngle=Επιλέξτε γωνία περιστροφής (σε πολλαπλάσια των 90 μοιρών):
|
||||
rotate.submit=Περιστροφή
|
||||
|
||||
|
||||
#split-pdfs
|
||||
@@ -891,8 +898,8 @@ watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%):
|
||||
watermark.selectText.8=Τύπος Υδατογραφήματος:
|
||||
watermark.selectText.9=Εικόνα Υδατογραφήματος:
|
||||
watermark.submit=Προσθήκη Υδατογραφήματος
|
||||
watermark.type.1=Text
|
||||
watermark.type.2=Image
|
||||
watermark.type.1=Κείμενο
|
||||
watermark.type.2=Εικόνα
|
||||
|
||||
|
||||
#Change permissions
|
||||
@@ -944,7 +951,8 @@ pdfToPDFA.title=PDF σε PDF/A
|
||||
pdfToPDFA.header=PDF σε PDF/A
|
||||
pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί OCRmyPDF για PDF/A μετατροπή
|
||||
pdfToPDFA.submit=Μετατροπή
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -985,68 +993,77 @@ PDFToXML.credit=Αυτή η υπηρεσία χρησιμοποιεί LibreOffic
|
||||
PDFToXML.submit=Μετατροπή
|
||||
|
||||
#PDFToCSV
|
||||
PDFToCSV.title=PDF ?? CSV
|
||||
PDFToCSV.header=PDF ?? CSV
|
||||
PDFToCSV.prompt=Choose page to extract table
|
||||
PDFToCSV.submit=?????????
|
||||
PDFToCSV.title=PDF σε CSV
|
||||
PDFToCSV.header=PDF σε CSV
|
||||
PDFToCSV.prompt=Επιλέξτε σελίδα για εξαγωγή πίνακα
|
||||
PDFToCSV.submit=Εξαξωγή
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.title=Split PDF by Size or Count
|
||||
split-by-size-or-count.header=Split PDF by Size or Count
|
||||
split-by-size-or-count.type.label=Select Split Type
|
||||
split-by-size-or-count.type.size=By Size
|
||||
split-by-size-or-count.type.pageCount=By Page Count
|
||||
split-by-size-or-count.type.docCount=By Document Count
|
||||
split-by-size-or-count.value.label=Enter Value
|
||||
split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5)
|
||||
split-by-size-or-count.submit=Submit
|
||||
split-by-size-or-count.title=Διαχωρίστε το PDF Κατά Μέγεθος ή Μέτρηση
|
||||
split-by-size-or-count.header=Διαχωρίστε το PDF Κατά Μέγεθος ή Μέτρηση
|
||||
split-by-size-or-count.type.label=Επιλέξτε Τύπος Διαχωρισμού
|
||||
split-by-size-or-count.type.size=Ανά Μέγεθος
|
||||
split-by-size-or-count.type.pageCount=Ανά αριθμό σελίδων
|
||||
split-by-size-or-count.type.docCount=Ανά αριθμό εγγράφων
|
||||
split-by-size-or-count.value.label=Εισαγάγετε τιμή
|
||||
split-by-size-or-count.value.placeholder=Εισαγάγετε μέγεθος (π.χ. 2MB ή 3KB) ή μέτρηση (π.χ. 5)
|
||||
split-by-size-or-count.submit=Υποβολή
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=Overlay PDF Files
|
||||
overlay-pdfs.baseFile.label=Select Base PDF File
|
||||
overlay-pdfs.overlayFiles.label=Select Overlay PDF Files
|
||||
overlay-pdfs.mode.label=Select Overlay Mode
|
||||
overlay-pdfs.mode.sequential=Sequential Overlay
|
||||
overlay-pdfs.mode.interleaved=Interleaved Overlay
|
||||
overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay
|
||||
overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode)
|
||||
overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1)
|
||||
overlay-pdfs.position.label=Select Overlay Position
|
||||
overlay-pdfs.header=Επικάλυψη αρχείων PDF
|
||||
overlay-pdfs.baseFile.label=Επιλέξτε Βασικό αρχείο PDF
|
||||
overlay-pdfs.overlayFiles.label=Επιλέξτε Επικάλυψη αρχείων PDF
|
||||
overlay-pdfs.mode.label=Επιλέξτε Λειτουργία Επικάλυψης
|
||||
overlay-pdfs.mode.sequential=Διαδοχική Επικάλυψη
|
||||
overlay-pdfs.mode.interleaved=Επικάλυψη με Παρεμβολή
|
||||
overlay-pdfs.mode.fixedRepeat=Διορθώθηκε η Επικάλυψη Επανάληψης
|
||||
overlay-pdfs.counts.label=Μετρήσεις επικάλυψης (για σταθερή λειτουργία επανάληψης)
|
||||
overlay-pdfs.counts.placeholder=Εισαγάγετε μετρήσεις διαχωρισμένες με κόμματα (π.χ. 2,3,1)
|
||||
overlay-pdfs.position.label=Επιλέξτε Θέση Επικάλυψης
|
||||
overlay-pdfs.position.foreground=Foreground
|
||||
overlay-pdfs.position.background=Background
|
||||
overlay-pdfs.submit=Submit
|
||||
overlay-pdfs.submit=Υποβολή
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=Split PDF by Sections
|
||||
split-by-sections.header=Split PDF into Sections
|
||||
split-by-sections.horizontal.label=Horizontal Divisions
|
||||
split-by-sections.vertical.label=Vertical Divisions
|
||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
split-by-sections.title=Διαχωρισμός PDF ανά ενότητες
|
||||
split-by-sections.header=Διαχωρίστε το PDF σε Ενότητες
|
||||
split-by-sections.horizontal.label=Οριζόντιες Διαιρέσεις
|
||||
split-by-sections.vertical.label=Κάθετες Διαιρέσεις
|
||||
split-by-sections.horizontal.placeholder=Εισαγάγετε τον αριθμό των οριζόντιων διαιρέσεων
|
||||
split-by-sections.vertical.placeholder=Εισαγάγετε τον αριθμό των κάθετων διαιρέσεων
|
||||
split-by-sections.submit=Διαχωρισμός PDF
|
||||
split-by-sections.merge=Συγχώνευση σε ένα PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
licenses.header=3rd Party Licenses
|
||||
licenses.nav=Άδειες
|
||||
licenses.title=3rd Party Άδειες
|
||||
licenses.header=3rd Party Άδειες
|
||||
licenses.module=Module
|
||||
licenses.version=Version
|
||||
licenses.license=License
|
||||
licenses.version=Εκδοχή
|
||||
licenses.license=Άδεια
|
||||
|
||||
|
||||
# error
|
||||
error.sorry=Sorry for the issue!
|
||||
error.needHelp=Need help / Found an issue?
|
||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
||||
error.404.1=We can't seem to find the page you're looking for.
|
||||
error.404.2=Something went wrong
|
||||
error.github=Submit a ticket on GitHub
|
||||
error.showStack=Show Stack Trace
|
||||
error.copyStack=Copy Stack Trace
|
||||
error.githubSubmit=GitHub - Submit a ticket
|
||||
error.discordSubmit=Discord - Submit Support post
|
||||
error.sorry=Συγγνώμη για το ζήτημα!
|
||||
error.needHelp=Χρειάζεστε βοήθεια / Βρήκατε πρόβλημα;
|
||||
error.contactTip=Εάν εξακολουθείτε να αντιμετωπίζετε προβλήματα, μη διστάσετε να επικοινωνήσετε μαζί μας για βοήθεια. Μπορείτε να υποβάλετε ένα ticket στη σελίδα μας στο GitHub ή να επικοινωνήσετε μαζί μας μέσω του Discord:
|
||||
error.404.head=404 - Η σελίδα δεν βρέθηκε | Oops, we tripped in the code!
|
||||
error.404.1=Δεν μπορούμε να βρούμε τη σελίδα που ψάχνετε.
|
||||
error.404.2=Κάτι πήγε στραβά
|
||||
error.github=Υποβάλετε ένα ticket στο GitHub
|
||||
error.showStack=Εμφάνιση Stack Trace
|
||||
error.copyStack=Αντιγραφή Stack Trace
|
||||
error.githubSubmit=GitHub - Υποβάλετε ένα ticket
|
||||
error.discordSubmit=Discord - Υποβάλετε ένα Support post
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
||||
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Settings
|
||||
settings.update=Update available
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=App Version:
|
||||
settings.downloadOption.title=Choose download option (For single file non zip downloads):
|
||||
settings.downloadOption.1=Open in same window
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Download file
|
||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
||||
settings.signOut=Sign Out
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=Change User's Role
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Remember me
|
||||
login.invalid=Invalid username or password.
|
||||
login.locked=Your account has been locked.
|
||||
login.signinTitle=Please sign in
|
||||
login.ssoSignIn=Login via Single Sign-on
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF To PDF/A
|
||||
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
|
||||
pdfToPDFA.submit=Convert
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
||||
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -116,6 +118,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Settings
|
||||
settings.update=Update available
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=App Version:
|
||||
settings.downloadOption.title=Choose download option (For single file non zip downloads):
|
||||
settings.downloadOption.1=Open in same window
|
||||
@@ -124,8 +127,9 @@ settings.downloadOption.3=Download file
|
||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
||||
settings.signOut=Sign Out
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -175,6 +179,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=Change User's Role
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -439,6 +444,8 @@ login.rememberme=Remember me
|
||||
login.invalid=Invalid username or password.
|
||||
login.locked=Your account has been locked.
|
||||
login.signinTitle=Please sign in
|
||||
login.ssoSignIn=Login via Single Sign-on
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -944,6 +951,7 @@ pdfToPDFA.header=PDF To PDF/A
|
||||
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
|
||||
pdfToPDFA.submit=Convert
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1027,6 +1035,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,20 +57,25 @@ usernameExistsMessage=El nuevo nombre de usuario está en uso.
|
||||
invalidUsernameMessage=Nombre de usuario no válido, El nombre de ususario debe contener únicamente números y caracteres alfabéticos.
|
||||
deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso.
|
||||
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
|
||||
downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual
|
||||
downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
oops=Ups!
|
||||
help=Help
|
||||
goHomepage=Go to Homepage
|
||||
joinDiscord=Join our Discord server
|
||||
seeDockerHub=See Docker Hub
|
||||
visitGithub=Visit Github Repository
|
||||
donate=Donate
|
||||
goHomepage=Ir a la página principal
|
||||
joinDiscord=Únase a nuestro servidor Discord
|
||||
seeDockerHub=Ver Docker Hub
|
||||
visitGithub=Visitar Repositorio de Github
|
||||
donate=Donar
|
||||
color=Color
|
||||
sponsor=Sponsor
|
||||
sponsor=Patrocinador
|
||||
info=Info
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
###############
|
||||
# Pipeline #
|
||||
###############
|
||||
@@ -79,8 +84,8 @@ pipeline.uploadButton=Cargar personalización
|
||||
pipeline.configureButton=Configurar
|
||||
pipeline.defaultOption=Personalizar
|
||||
pipeline.submitButton=Enviar
|
||||
pipeline.help=Pipeline Help
|
||||
pipeline.scanHelp=Folder Scanning Help
|
||||
pipeline.help=Ayuda de Canalización
|
||||
pipeline.scanHelp=Ayuda de escaneado de carpetas
|
||||
|
||||
######################
|
||||
# Pipeline Options #
|
||||
@@ -117,6 +122,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Configuración
|
||||
settings.update=Actualización disponible
|
||||
settings.updateAvailable={0} es la versión instalada. Hay disponible una versión nueva ({1}).
|
||||
settings.appVersion=Versión de la aplicación:
|
||||
settings.downloadOption.title=Elegir la opción de descarga (para descargas de un solo archivo sin ZIP):
|
||||
settings.downloadOption.1=Abrir en la misma ventana
|
||||
@@ -125,8 +131,9 @@ settings.downloadOption.3=Descargar el archivo
|
||||
settings.zipThreshold=Archivos ZIP cuando excede el número de archivos descargados
|
||||
settings.signOut=Desconectar
|
||||
settings.accountSettings=Configuración de la cuenta
|
||||
|
||||
|
||||
settings.bored.help=Habilita el juego del huevo de pascua
|
||||
settings.cacheInputs.name=Guardar entradas del formulario
|
||||
settings.cacheInputs.help=Habilitar guardar entradas previamente utilizadas para futuras acciones
|
||||
|
||||
changeCreds.title=Cambiar Credenciales
|
||||
changeCreds.header=Actualice los detalles de su cuenta
|
||||
@@ -176,6 +183,7 @@ adminUserSettings.demoUser=Usuario Demo (Sin ajustes personalizados)
|
||||
adminUserSettings.internalApiUser=Usuario interno de API
|
||||
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
|
||||
adminUserSettings.submit=Guardar Usuario
|
||||
adminUserSettings.changeUserRole=Cambiar rol de usuario
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -250,7 +258,7 @@ compressPdfs.tags=aplastar,pequeño,diminuto
|
||||
|
||||
home.changeMetadata.title=Cambiar metadatos
|
||||
home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF
|
||||
changeMetadata.tags==Título,autor,fecha,creación,hora,editorial,productor,estadísticas
|
||||
changeMetadata.tags=título,autor,fecha,creación,hora,editorial,productor,estadísticas
|
||||
|
||||
home.fileToPDF.title=Convertir archivo a PDF
|
||||
home.fileToPDF.desc=Convertir casi cualquier archivo a PDF (DOCX, PNG, XLS, PPT, TXT y más)
|
||||
@@ -440,6 +448,8 @@ login.rememberme=Recordarme
|
||||
login.invalid=Nombre de usuario o contraseña erróneos.
|
||||
login.locked=Su cuenta se ha bloqueado.
|
||||
login.signinTitle=Por favor, inicie sesión
|
||||
login.ssoSignIn=Iniciar sesión a través del inicio de sesión único
|
||||
login.oauth2AutoCreateDisabled=Usuario DE creación automática de OAUTH2 DESACTIVADO
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -776,23 +786,23 @@ merge.submit=Unir
|
||||
pdfOrganiser.title=Organizador de páginas
|
||||
pdfOrganiser.header=Organizador de páginas PDF
|
||||
pdfOrganiser.submit=Organizar páginas
|
||||
pdfOrganiser.mode=Mode
|
||||
pdfOrganiser.mode.1=Custom Page Order
|
||||
pdfOrganiser.mode.2=Reverse Order
|
||||
pdfOrganiser.mode.3=Duplex Sort
|
||||
pdfOrganiser.mode.4=Booklet Sort
|
||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
||||
pdfOrganiser.mode.6=Odd-Even Split
|
||||
pdfOrganiser.mode.7=Remove First
|
||||
pdfOrganiser.mode.8=Remove Last
|
||||
pdfOrganiser.mode.9=Remove First and Last
|
||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
||||
pdfOrganiser.mode=Modo
|
||||
pdfOrganiser.mode.1=Orden de páginas personalizado
|
||||
pdfOrganiser.mode.2=Orden inverso
|
||||
pdfOrganiser.mode.3=Ordenar dúplex
|
||||
pdfOrganiser.mode.4=Ordenar folleto
|
||||
pdfOrganiser.mode.5=Orden de folleto de encuadernado lateral
|
||||
pdfOrganiser.mode.6=División par-impar
|
||||
pdfOrganiser.mode.7=Quitar primera
|
||||
pdfOrganiser.mode.8=Quitar última
|
||||
pdfOrganiser.mode.9=Quitar primera y última
|
||||
pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1)
|
||||
|
||||
|
||||
#multiTool
|
||||
multiTool.title=Multi-herramienta PDF
|
||||
multiTool.header=Multi-herramienta PDF
|
||||
multiTool.uploadPrompts=Please Upload PDF
|
||||
multiTool.uploadPrompts=Por favor, cargue PDF
|
||||
|
||||
#view pdf
|
||||
viewPdf.title=Ver PDF
|
||||
@@ -891,8 +901,8 @@ watermark.selectText.7=Opacidad (0% - 100%):
|
||||
watermark.selectText.8=Tipo de marca de agua:
|
||||
watermark.selectText.9=Imagen de marca de agua:
|
||||
watermark.submit=Añadir marca de agua
|
||||
watermark.type.1=Text
|
||||
watermark.type.2=Image
|
||||
watermark.type.1=Texto
|
||||
watermark.type.2=Imagen
|
||||
|
||||
|
||||
#Change permissions
|
||||
@@ -944,7 +954,8 @@ pdfToPDFA.title=PDF a PDF/A
|
||||
pdfToPDFA.header=PDF a PDF/A
|
||||
pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A
|
||||
pdfToPDFA.submit=Convertir
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1039,15 @@ split-by-sections.vertical.placeholder=Introduzca el número de divisiones verti
|
||||
split-by-sections.submit=Dividir PDF
|
||||
split-by-sections.merge=Unir en Un PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Imprimir archivo
|
||||
printFile.header=Imprimir archivo en la impresora
|
||||
printFile.selectText.1=Seleccionar archivo para imprimir
|
||||
printFile.selectText.2=Introducir nombre de la impresora
|
||||
printFile.submit=Imprimir
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licencias
|
||||
licenses.title=Licencias de terceros
|
||||
@@ -1038,15 +1058,15 @@ licenses.license=Licencia
|
||||
|
||||
|
||||
# error
|
||||
error.sorry=Sorry for the issue!
|
||||
error.needHelp=Need help / Found an issue?
|
||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
||||
error.404.1=We can't seem to find the page you're looking for.
|
||||
error.404.2=Something went wrong
|
||||
error.github=Submit a ticket on GitHub
|
||||
error.showStack=Show Stack Trace
|
||||
error.copyStack=Copy Stack Trace
|
||||
error.githubSubmit=GitHub - Submit a ticket
|
||||
error.discordSubmit=Discord - Submit Support post
|
||||
error.sorry=¡Perdón por el fallo!
|
||||
error.needHelp=Necesita ayuda / Encontró un fallo?
|
||||
error.contactTip=Si sigue experimentando errores, no dude en contactarnos para solicitar soporte. Puede enviarnos un ticket en la página de GitHub o contactarnos mediante Discord:
|
||||
error.404.head=404 - Página no encontrada | Ups, tropezamos con el código!
|
||||
error.404.1=Parece que no podemos encontrar la página que está buscando.
|
||||
error.404.2=Algo salió mal
|
||||
error.github=Envíe un ticket en GitHub
|
||||
error.showStack=Mostrar seguimiento de pila
|
||||
error.copyStack=Mostrar seguimiento de pila
|
||||
error.githubSubmit=GitHub - Enviar un ticket
|
||||
error.discordSubmit=Discord - Enviar mensaje de soporte
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi
|
||||
downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Ezarpenak
|
||||
settings.update=Eguneratze eskuragarria
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Aplikazioaren bertsioa:
|
||||
settings.downloadOption.title=Hautatu deskargatzeko aukera (fitxategi bakarra deskargatzeko ZIP gabe):
|
||||
settings.downloadOption.1=Ireki leiho berean
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Deskargatu fitxategia
|
||||
settings.zipThreshold=ZIP fitxategiak deskargatutako fitxategi kopurua gainditzen denean
|
||||
settings.signOut=Saioa itxi
|
||||
settings.accountSettings=Kontuaren ezarpenak
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Gorde Erabiltzailea
|
||||
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Oroitu nazazu
|
||||
login.invalid=Okerreko erabiltzaile izena edo pasahitza.
|
||||
login.locked=Zure kontua blokeatu egin da.
|
||||
login.signinTitle=Mesedez, hasi saioa
|
||||
login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDFa PDF/A bihurtu
|
||||
pdfToPDFA.credit=Zerbitzu honek OCRmyPDF erabiltzen du PDFak PDF/A bihurtzeko
|
||||
pdfToPDFA.submit=Bihurtu
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Le nouveau nom d’utilisateur existe déjà.
|
||||
invalidUsernameMessage=Nom d’utilisateur invalide, le nom d’utilisateur ne peut contenir que des chiffres et des lettres.
|
||||
deleteCurrentUserMessage=Impossible de supprimer l’utilisateur actuellement connecté.
|
||||
deleteUsernameExistsMessage=Le nom d’utilisateur n’existe pas et ne peut pas être supprimé.
|
||||
downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel
|
||||
downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché.
|
||||
error=Erreur
|
||||
oops=Oups !
|
||||
help=Aide
|
||||
@@ -70,7 +72,6 @@ sponsor=Sponsor
|
||||
info=Info
|
||||
|
||||
|
||||
|
||||
###############
|
||||
# Pipeline #
|
||||
###############
|
||||
@@ -117,6 +118,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Paramètres
|
||||
settings.update=Mise à jour disponible
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Version de l’application :
|
||||
settings.downloadOption.title=Choisissez l’option de téléchargement (pour les téléchargements à fichier unique non ZIP) :
|
||||
settings.downloadOption.1=Ouvrir dans la même fenêtre
|
||||
@@ -125,8 +127,9 @@ settings.downloadOption.3=Télécharger le fichier
|
||||
settings.zipThreshold=Compresser les fichiers en ZIP lorsque le nombre de fichiers téléchargés dépasse
|
||||
settings.signOut=Déconnexion
|
||||
settings.accountSettings=Paramètres du compte
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Modifiez vos identifiants
|
||||
changeCreds.header=Mettez à jour vos identifiants de connexion
|
||||
@@ -176,6 +179,7 @@ adminUserSettings.demoUser=Demo User (Paramètres par défaut)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Forcer l’utilisateur à changer son nom d’utilisateur/mot de passe lors de la connexion
|
||||
adminUserSettings.submit=Ajouter
|
||||
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +444,8 @@ login.rememberme=Se souvenir de moi
|
||||
login.invalid=Nom d’utilisateur ou mot de passe invalide.
|
||||
login.locked=Votre compte a été verrouillé.
|
||||
login.signinTitle=Veuillez vous connecter
|
||||
login.ssoSignIn=Se connecter via l'authentification unique
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Création automatique d'utilisateur désactivée
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +951,7 @@ pdfToPDFA.header=PDF en PDF/A
|
||||
pdfToPDFA.credit=Ce service utilise OCRmyPDF pour la conversion en PDF/A.
|
||||
pdfToPDFA.submit=Convertir
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1035,15 @@ split-by-sections.vertical.placeholder=Entrer le nombre de divisions verticales
|
||||
split-by-sections.submit=Diviser le PDF
|
||||
split-by-sections.merge=Fusionner en un seul PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licences
|
||||
licenses.title=Licences tierces
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=नया उपयोगकर्ता नाम पह
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
|
||||
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=सेटिंग्स
|
||||
settings.update=अपडेट उपलब्ध है
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=ऐप संस्करण:
|
||||
settings.downloadOption.title=डाउनलोड विकल्प चुनें (एकल फ़ाइल गैर-ज़िप डाउनलोड के लिए):
|
||||
settings.downloadOption.1=एक ही विंडो में खोलें
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=फ़ाइल डाउनलोड करें
|
||||
settings.zipThreshold=जब डाउनलोड की गई फ़ाइलों की संख्या सीमा से अधिक हो
|
||||
settings.signOut=साइन आउट
|
||||
settings.accountSettings=खाता सेटिंग्स
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=क्रेडेंशियल बदलें
|
||||
changeCreds.header=अपना खाता विवरण अपडेट करें
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें
|
||||
adminUserSettings.submit=उपयोगकर्ता को सहेजें
|
||||
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=मुझे याद रखें
|
||||
login.invalid=अमान्य उपयोगकर्ता नाम या पासवर्ड।
|
||||
login.locked=आपका खाता लॉक कर दिया गया है।
|
||||
login.signinTitle=कृपया साइन इन करें
|
||||
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 ऑटो - क्रिएट यूज़र अक्षम किया गया
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF से PDF/A में
|
||||
pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए OCRmyPDF का उपयोग किया जाता है।
|
||||
pdfToPDFA.submit=परिवर्तित करें
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=लंबवत विभाजन की
|
||||
split-by-sections.submit=PDF को विभाजित करें
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Az új felhasználónév már létezik.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni
|
||||
downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Beállítások
|
||||
settings.update=Frisítés elérhető
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=App Verzió:
|
||||
settings.downloadOption.title=Válassza ki a letöltési lehetőséget (Egyetlen fájl esetén a nem tömörített letöltésekhez):
|
||||
settings.downloadOption.1=Nyissa meg ugyanabban az ablakban
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Töltse le a fájlt
|
||||
settings.zipThreshold=Fájlok tömörítése, ha a letöltött fájlok száma meghaladja
|
||||
settings.signOut=Kijelentkezés
|
||||
settings.accountSettings=Fiókbeállítások
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Hitelesítés megváltoztatása
|
||||
changeCreds.header=Frissítse fiókadatait
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználónév/jelszó megváltoztatására bejelentkezéskor
|
||||
adminUserSettings.submit=Felhasználó mentése
|
||||
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Emlékezz rám
|
||||
login.invalid=Érvénytelen felhasználónév vagy jelszó!
|
||||
login.locked=A fiókja zárolva lett!
|
||||
login.signinTitle=Kérjük, jelentkezzen be!
|
||||
login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Felhasználó automatikus létrehozása letiltva
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF >> PDF/A
|
||||
pdfToPDFA.credit=Ez a szolgáltatás az OCRmyPDF-t használja a PDF/A konverzióhoz
|
||||
pdfToPDFA.submit=Konvertálás
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Adja meg a függőleges szakaszok számá
|
||||
split-by-sections.submit=Felosztás
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Nama pengguna baru sudah ada.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini
|
||||
downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Pengaturan
|
||||
settings.update=Pembaruan tersedia
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Versi Aplikasi:
|
||||
settings.downloadOption.title=Pilih opsi unduhan (Untuk unduhan berkas tunggal non zip):
|
||||
settings.downloadOption.1=Buka di jendela yang sama
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Unduh berkas
|
||||
settings.zipThreshold=Berkas zip ketika jumlah berkas yang diunduh melebihi
|
||||
settings.signOut=Keluar
|
||||
settings.accountSettings=Pengaturan Akun
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Ubah Kredensial
|
||||
changeCreds.header=Perbarui Detail Akun Anda
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk
|
||||
adminUserSettings.submit=Simpan Pengguna
|
||||
adminUserSettings.changeUserRole=Ubah Peran Pengguna
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Ingat saya
|
||||
login.invalid=Nama pengguna atau kata sandi tidak valid.
|
||||
login.locked=Akun Anda telah dikunci.
|
||||
login.signinTitle=Silakan masuk
|
||||
login.ssoSignIn=Masuk melalui Single Sign - on
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Buat Otomatis Pengguna Dinonaktifkan
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF ke PDF/A
|
||||
pdfToPDFA.credit=Layanan ini menggunakan OCRmyPDF untuk konversi PDF/A.
|
||||
pdfToPDFA.submit=Konversi
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Input angka untuk pembagian vertikal
|
||||
split-by-sections.submit=Pisahkan PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Il nuovo nome utente esiste già.
|
||||
invalidUsernameMessage=Nome utente non valido, il nome utente deve contenere solo caratteri alfabetici e numeri.
|
||||
deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
|
||||
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
|
||||
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
|
||||
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
|
||||
error=Errore
|
||||
oops=Oops!
|
||||
help=Aiuto
|
||||
@@ -81,6 +83,7 @@ pipeline.defaultOption=Personalizzato
|
||||
pipeline.submitButton=Invia
|
||||
pipeline.help=Aiuto sulla pipeline
|
||||
pipeline.scanHelp=Aiuto per la scansione delle cartelle
|
||||
|
||||
######################
|
||||
# Pipeline Options #
|
||||
######################
|
||||
@@ -116,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Impostazioni
|
||||
settings.update=Aggiornamento disponibile
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Versione App:
|
||||
settings.downloadOption.title=Scegli opzione di download (Per file singoli non compressi):
|
||||
settings.downloadOption.1=Apri in questa finestra
|
||||
@@ -124,8 +128,9 @@ settings.downloadOption.3=Scarica file
|
||||
settings.zipThreshold=Comprimi file in .zip quando il numero di download supera
|
||||
settings.signOut=Logout
|
||||
settings.accountSettings=Impostazioni Account
|
||||
|
||||
|
||||
settings.bored.help=Abilita easter egg game
|
||||
settings.cacheInputs.name=Salva gli input del modulo
|
||||
settings.cacheInputs.help=Abilitare per memorizzare gli input utilizzati in precedenza per esecuzioni future
|
||||
|
||||
changeCreds.title=Cambia credenziali
|
||||
changeCreds.header=Aggiorna i dettagli del tuo account
|
||||
@@ -175,6 +180,7 @@ adminUserSettings.demoUser=Utente demo (nessuna impostazione personalizzata)
|
||||
adminUserSettings.internalApiUser=API utente interna
|
||||
adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso
|
||||
adminUserSettings.submit=Salva utente
|
||||
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -193,7 +199,7 @@ multiTool.tags=Strumento multiplo,operazione multipla,interfaccia utente,trascin
|
||||
|
||||
home.merge.title=Unisci
|
||||
home.merge.desc=Unisci facilmente più PDF in uno.
|
||||
merge.tags=unione,operazioni sulla pagina,back end,lato server
|
||||
merge.tags=unione,operazioni sulla pagina,back-end,lato server
|
||||
|
||||
home.split.title=Dividi
|
||||
home.split.desc=Dividi un singolo PDF in più documenti.
|
||||
@@ -439,6 +445,8 @@ login.rememberme=Ricordami
|
||||
login.invalid=Nome utente o password errati.
|
||||
login.locked=Il tuo account è stato bloccato.
|
||||
login.signinTitle=Per favore accedi
|
||||
login.ssoSignIn=Accedi tramite Single Sign-on
|
||||
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -648,7 +656,7 @@ removeBlanks.submit=Rimuovi
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Rimuovi Annotazioni
|
||||
removeAnnotations.header=Remuovi Annotazioni
|
||||
removeAnnotations.header=Rimuovi Annotazioni
|
||||
removeAnnotations.submit=Rimuovi
|
||||
|
||||
|
||||
@@ -944,6 +952,7 @@ pdfToPDFA.header=Da PDF a PDF/A
|
||||
pdfToPDFA.credit=Questo servizio utilizza OCRmyPDF per la conversione in PDF/A.
|
||||
pdfToPDFA.submit=Converti
|
||||
pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente
|
||||
pdfToPDFA.outputFormat=Formato di output
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1027,6 +1036,15 @@ split-by-sections.vertical.placeholder=Inserire il numero di divisioni verticali
|
||||
split-by-sections.submit=Dividi PDF
|
||||
split-by-sections.merge=Unisci in un unico PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Stampa file
|
||||
printFile.header=Stampa file su stampante
|
||||
printFile.selectText.1=Seleziona file da stampare
|
||||
printFile.selectText.2=Inserire il nome della stampante
|
||||
printFile.submit=Stampare
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenze
|
||||
licenses.title=Licenze di terze parti
|
||||
|
||||
@@ -57,16 +57,18 @@ usernameExistsMessage=新しいユーザー名はすでに存在します。
|
||||
invalidUsernameMessage=ユーザー名が無効です。ユーザー名にはアルファベットと数字のみを使用してください。
|
||||
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
|
||||
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
goHomepage=Go to Homepage
|
||||
joinDiscord=Join our Discord server
|
||||
seeDockerHub=See Docker Hub
|
||||
visitGithub=Visit Github Repository
|
||||
donate=Donate
|
||||
color=Color
|
||||
sponsor=Sponsor
|
||||
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
|
||||
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
|
||||
error=エラー
|
||||
oops=おっと!
|
||||
help=ヘルプ
|
||||
goHomepage=ホームページへ移動
|
||||
joinDiscord=Discordサーバーに参加する
|
||||
seeDockerHub=Docker Hubを見る
|
||||
visitGithub=Githubリポジトリを訪問する
|
||||
donate=寄付する
|
||||
color=色
|
||||
sponsor=スポンサー
|
||||
info=Info
|
||||
|
||||
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=設定
|
||||
settings.update=利用可能なアップデート
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Appバージョン:
|
||||
settings.downloadOption.title=ダウンロードオプション (zip以外の単一ファイル):
|
||||
settings.downloadOption.1=同じウィンドウで開く
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=ファイルをダウンロード
|
||||
settings.zipThreshold=このファイル数を超えたときにファイルを圧縮する
|
||||
settings.signOut=サインアウト
|
||||
settings.accountSettings=アカウント設定
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=資格情報の変更
|
||||
changeCreds.header=アカウントの詳細を更新する
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=デモユーザー (カスタム設定なし)
|
||||
adminUserSettings.internalApiUser=内部APIユーザー
|
||||
adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する
|
||||
adminUserSettings.submit=ユーザーの保存
|
||||
adminUserSettings.changeUserRole=ユーザーの役割を変更する
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=サインイン状態を記憶する
|
||||
login.invalid=ユーザー名かパスワードが無効です。
|
||||
login.locked=あなたのアカウントはロックされています。
|
||||
login.signinTitle=サインインしてください
|
||||
login.ssoSignIn=シングルサインオンでログイン
|
||||
login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDFをPDF/Aに変換
|
||||
pdfToPDFA.credit=本サービスはPDF/Aの変換にOCRmyPDFを使用しています。
|
||||
pdfToPDFA.submit=変換
|
||||
pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=垂直方向の分割数を選択
|
||||
split-by-sections.submit=分割
|
||||
split-by-sections.merge=1 つの PDF に結合するかどうか
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=ライセンス
|
||||
licenses.title=サードパーティライセンス
|
||||
@@ -1038,15 +1055,15 @@ licenses.license=ライセンス
|
||||
|
||||
|
||||
# error
|
||||
error.sorry=Sorry for the issue!
|
||||
error.needHelp=Need help / Found an issue?
|
||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
||||
error.404.1=We can't seem to find the page you're looking for.
|
||||
error.404.2=Something went wrong
|
||||
error.github=Submit a ticket on GitHub
|
||||
error.showStack=Show Stack Trace
|
||||
error.copyStack=Copy Stack Trace
|
||||
error.githubSubmit=GitHub - Submit a ticket
|
||||
error.discordSubmit=Discord - Submit Support post
|
||||
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 - サポート投稿を提出
|
||||
|
||||
|
||||
@@ -57,17 +57,20 @@ usernameExistsMessage=새 사용자명이 이미 존재합니다.
|
||||
invalidUsernameMessage=사용자 이름이 잘못되었습니다. 사용자 이름에는 알파벳 문자와 숫자만 포함되어야 합니다.
|
||||
deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다.
|
||||
deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
goHomepage=Go to Homepage
|
||||
joinDiscord=Join our Discord server
|
||||
seeDockerHub=See Docker Hub
|
||||
visitGithub=Visit Github Repository
|
||||
donate=Donate
|
||||
color=Color
|
||||
sponsor=Sponsor
|
||||
|
||||
info=Info
|
||||
downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다
|
||||
downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다.
|
||||
error=오류
|
||||
oops=어머나!
|
||||
help=도움말
|
||||
goHomepage=홈페이지로 이동
|
||||
joinDiscord=Discord 서버에 참여하기
|
||||
seeDockerHub=Docker Hub에서 확인하기
|
||||
visitGithub=GitHub 저장소 방문하기
|
||||
donate=기부하기
|
||||
color=색상
|
||||
sponsor=스폰서
|
||||
|
||||
|
||||
|
||||
@@ -117,6 +120,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=설정
|
||||
settings.update=업데이트 가능
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=앱 버전:
|
||||
settings.downloadOption.title=다운로드 옵션 선택 (zip 파일이 아닌 단일 파일 다운로드 시):
|
||||
settings.downloadOption.1=현재 창에서 열기
|
||||
@@ -125,8 +129,9 @@ settings.downloadOption.3=다운로드
|
||||
settings.zipThreshold=다운로드한 파일 수가 초과된 경우 파일 압축하기
|
||||
settings.signOut=로그아웃
|
||||
settings.accountSettings=계정 설정
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=계정 정보 변경
|
||||
changeCreds.header=계정 정보 업데이트
|
||||
@@ -176,6 +181,7 @@ adminUserSettings.demoUser=데모 사용자(사용자 지정 설정 없음)
|
||||
adminUserSettings.internalApiUser=내부 API 사용자
|
||||
adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제
|
||||
adminUserSettings.submit=사용자 저장
|
||||
adminUserSettings.changeUserRole=사용자의 역할 변경
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +446,8 @@ login.rememberme=로그인 유지
|
||||
login.invalid=사용자 이름이나 비밀번호가 틀립니다.
|
||||
login.locked=계정이 잠겼습니다.
|
||||
login.signinTitle=로그인해 주세요.
|
||||
login.ssoSignIn=싱글사인온을 통한 로그인
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 사용자 자동 생성 비활성화됨
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +953,7 @@ pdfToPDFA.header=PDF 문서를 PDF/A로 변환
|
||||
pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 OCRmyPDF 문서를 사용합니다.
|
||||
pdfToPDFA.submit=변환
|
||||
pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다.
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1037,15 @@ split-by-sections.vertical.placeholder=수직 분할 수를 입력합니다
|
||||
split-by-sections.submit=PDF 분할
|
||||
split-by-sections.merge=하나의 PDF로 병합
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=라이센스
|
||||
licenses.title=제3자 라이선스
|
||||
@@ -1038,15 +1056,15 @@ licenses.license=라이센스
|
||||
|
||||
|
||||
# error
|
||||
error.sorry=Sorry for the issue!
|
||||
error.needHelp=Need help / Found an issue?
|
||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
||||
error.404.1=We can't seem to find the page you're looking for.
|
||||
error.404.2=Something went wrong
|
||||
error.github=Submit a ticket on GitHub
|
||||
error.showStack=Show Stack Trace
|
||||
error.copyStack=Copy Stack Trace
|
||||
error.githubSubmit=GitHub - Submit a ticket
|
||||
error.discordSubmit=Discord - Submit Support post
|
||||
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 - 문의 게시
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Nieuwe gebruikersnaam bestaat al.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden
|
||||
downgradeCurrentUserLongMessage=Kan de rol van de huidige gebruiker niet downgraden. Huidige gebruiker wordt dus niet weergegeven.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Instellingen
|
||||
settings.update=Update beschikbaar
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=App versie:
|
||||
settings.downloadOption.title=Kies download optie (Voor enkelvoudige bestanddownloads zonder zip):
|
||||
settings.downloadOption.1=Open in hetzelfde venster
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Download bestand
|
||||
settings.zipThreshold=Bestanden zippen wanneer het aantal gedownloade bestanden meer is dan
|
||||
settings.signOut=Uitloggen
|
||||
settings.accountSettings=Account instellingen
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Inloggegevens wijzigen
|
||||
changeCreds.header=Werk je accountgegevens bij
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demogebruiker (geen aangepaste instellingen)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen
|
||||
adminUserSettings.submit=Gebruiker opslaan
|
||||
adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Onthoud mij
|
||||
login.invalid=Ongeldige gebruikersnaam of wachtwoord.
|
||||
login.locked=Je account is geblokkeerd.
|
||||
login.signinTitle=Gelieve in te loggen
|
||||
login.ssoSignIn=Inloggen via Single Sign-on
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Automatisch aanmaken gebruiker uitgeschakeld
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF naar PDF/A
|
||||
pdfToPDFA.credit=Deze service gebruikt OCRmyPDF voor PDF/A-conversie
|
||||
pdfToPDFA.submit=Converteren
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Voer het aantal verticale secties in
|
||||
split-by-sections.submit=PDF splitsen
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenties
|
||||
licenses.title=Licenties van derden
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Nie można obniżyć roli 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.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Ustawienia
|
||||
settings.update=Dostępna aktualizacja
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Wersia aplikacji:
|
||||
settings.downloadOption.title=Wybierz opcję pobierania (w przypadku pobierania pojedynczych plików innych niż ZIP):
|
||||
settings.downloadOption.1=Otwórz w tym samym oknie
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Pobierz plik
|
||||
settings.zipThreshold=Spakuj pliki, gdy liczba pobranych plików przekroczy
|
||||
settings.signOut=Sign Out
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=Zmień rolę użytkownika
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Remember me
|
||||
login.invalid=Invalid username or password.
|
||||
login.locked=Your account has been locked.
|
||||
login.signinTitle=Please sign in
|
||||
login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego
|
||||
login.oauth2AutoCreateDisabled=Wyłączono automatyczne tworzenie użytkownika OAUTH2
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF na PDF/A
|
||||
pdfToPDFA.credit=Ta usługa używa OCRmyPDF do konwersji PDF/A
|
||||
pdfToPDFA.submit=Konwertuj
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário atual
|
||||
downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do usuário atual. Portanto, o usuário atual não será mostrado.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Configurações
|
||||
settings.update=Atualização disponível
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Versão do aplicativo:
|
||||
settings.downloadOption.title=Escolha a opção de download (para downloads não compactados de arquivo único):
|
||||
settings.downloadOption.1=Abrir na mesma janela
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=⇬ Fazer download do arquivo
|
||||
settings.zipThreshold=Compactar arquivos quando o número de arquivos baixados exceder
|
||||
settings.signOut=Sign Out
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=Alterar Função de Usuário
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Remember me
|
||||
login.invalid=Invalid username or password.
|
||||
login.locked=Your account has been locked.
|
||||
login.signinTitle=Please sign in
|
||||
login.ssoSignIn=Iniciar sessão através de início de sessão único
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Criar Usuário Desativado
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF para PDF/A
|
||||
pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A
|
||||
pdfToPDFA.submit=Converter
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Esse utilizador já existe.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Não é possível fazer downgrade da função do utilizador atual
|
||||
downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do utilizador atual. Portanto, o utilizador atual não será mostrado.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Configurações
|
||||
settings.update=Atualização disponível
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Versão da aplicação:
|
||||
settings.downloadOption.title=Escolha a opção de download (para downloads não compactados de ficheiro único):
|
||||
settings.downloadOption.1=Abrir na mesma janela
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=⇬ Fazer download do ficheiro
|
||||
settings.zipThreshold=Compactar ficheiros quando o número de ficheiros baixados exceder
|
||||
settings.signOut=Terminar Sessão
|
||||
settings.accountSettings=Configuração de Conta
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Alterar senha
|
||||
changeCreds.header=Alterar dados da sua conta
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=Alterar usuário
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Lembrar dados
|
||||
login.invalid=Utilizador ou senha inválidos.
|
||||
login.locked=A sua conta foi bloqueada.
|
||||
login.signinTitle=Introduza os seus dados de acesso
|
||||
login.ssoSignIn=Iniciar sessão através de início de sessão único
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Criação Automática de Utilizador Desativada
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF para PDF/A
|
||||
pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A
|
||||
pdfToPDFA.submit=Converter
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Introduza o número de divisões verticai
|
||||
split-by-sections.submit=Dividir PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenças
|
||||
licenses.title=Licenças de terceiros
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Rolul utilizatorului curent nu poate fi retrogradat
|
||||
downgradeCurrentUserLongMessage=Rolul utilizatorului curent nu poate fi retrogradat. Prin urmare, utilizatorul curent nu va fi afișat.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Setări
|
||||
settings.update=Actualizare disponibilă
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Versiune aplicație:
|
||||
settings.downloadOption.title=Alege opțiunea de descărcare (pentru descărcarea unui singur fișier non-zip):
|
||||
settings.downloadOption.1=Deschide în aceeași fereastră
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Descarcă fișierul
|
||||
settings.zipThreshold=Împachetează fișierele când numărul de fișiere descărcate depășește
|
||||
settings.signOut=Sign Out
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=Schimbați rolul utilizatorului
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Remember me
|
||||
login.invalid=Invalid username or password.
|
||||
login.locked=Your account has been locked.
|
||||
login.signinTitle=Please sign in
|
||||
login.ssoSignIn=Conectare prin conectare unică
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Creare automată utilizator dezactivată
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF către PDF/A
|
||||
pdfToPDFA.credit=Acest serviciu utilizează OCRmyPDF pentru conversia în PDF/A
|
||||
pdfToPDFA.submit=Convert
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,17 +57,20 @@ usernameExistsMessage=Новое имя пользователя уже суще
|
||||
invalidUsernameMessage=Недопустимое имя пользователя, Имя пользователя должно содержать только буквы алфавита и цифры.
|
||||
deleteCurrentUserMessage=Невозможно удалить пользователя, вошедшего в систему.
|
||||
deleteUsernameExistsMessage=Имя пользователя не существует и не может быть удалено.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
goHomepage=Go to Homepage
|
||||
joinDiscord=Join our Discord server
|
||||
seeDockerHub=See Docker Hub
|
||||
visitGithub=Visit Github Repository
|
||||
donate=Donate
|
||||
color=Color
|
||||
sponsor=Sponsor
|
||||
info=Info
|
||||
downgradeCurrentUserMessage=Невозможно понизить роль текущего пользователя
|
||||
downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться.
|
||||
error=Ошибка
|
||||
oops=Ой!
|
||||
help=Помощь
|
||||
goHomepage=Перейти на главную страницу
|
||||
joinDiscord=Присоединиться к нашему серверу Discord
|
||||
seeDockerHub=Посмотреть в Docker Hub
|
||||
visitGithub=Посетить репозиторий на GitHub
|
||||
donate=Пожертвовать
|
||||
color=Цвет
|
||||
sponsor=Спонсор
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -117,6 +120,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Настройки
|
||||
settings.update=Доступно обновление
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Версия приложения:
|
||||
settings.downloadOption.title=Выберите вариант загрузки (для загрузки одного файла без zip):
|
||||
settings.downloadOption.1=Открыть в том же окне
|
||||
@@ -125,8 +129,9 @@ settings.downloadOption.3=Загрузить файл
|
||||
settings.zipThreshold=Zip-файлы, когда количество загруженных файлов превышает
|
||||
settings.signOut=Выйти
|
||||
settings.accountSettings=Настройки аккаунта
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Изменить учетные данные
|
||||
changeCreds.header=Обновите данные вашей учетной записи
|
||||
@@ -176,6 +181,7 @@ adminUserSettings.demoUser=Демо-пользователь (без настр
|
||||
adminUserSettings.internalApiUser=Внутренний пользователь API
|
||||
adminUserSettings.forceChange=Просить пользователя изменить пароль при входе в систему
|
||||
adminUserSettings.submit=Сохранить пользователя
|
||||
adminUserSettings.changeUserRole=Изменить роль пользователя
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +446,8 @@ login.rememberme=Запомнить меня
|
||||
login.invalid=Недействительное имя пользователя или пароль.
|
||||
login.locked=Ваша учетная запись заблокирована.
|
||||
login.signinTitle=Пожалуйста, войдите
|
||||
login.ssoSignIn=Вход через единый вход
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Автоматическое создание пользователя отключено
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +953,7 @@ pdfToPDFA.header=PDF в PDF/A
|
||||
pdfToPDFA.credit=Этот сервис использует OCRmyPDF для преобразования PDF/A
|
||||
pdfToPDFA.submit=Конвертировать
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1037,15 @@ split-by-sections.vertical.placeholder=Введите количество ве
|
||||
split-by-sections.submit=Разделить PDF
|
||||
split-by-sections.merge=Объединить в один PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Лицензии
|
||||
licenses.title=Лицензии от третьих сторон
|
||||
@@ -1038,15 +1056,15 @@ licenses.license=Лицензия
|
||||
|
||||
|
||||
# error
|
||||
error.sorry=Sorry for the issue!
|
||||
error.needHelp=Need help / Found an issue?
|
||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
||||
error.404.1=We can't seem to find the page you're looking for.
|
||||
error.404.2=Something went wrong
|
||||
error.github=Submit a ticket on GitHub
|
||||
error.showStack=Show Stack Trace
|
||||
error.copyStack=Copy Stack Trace
|
||||
error.githubSubmit=GitHub - Submit a ticket
|
||||
error.discordSubmit=Discord - Submit Support post
|
||||
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 - Отправить запрос в поддержку
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Novi korisnik već postoji
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Nije moguće degradirati ulogu trenutnog korisnika
|
||||
downgradeCurrentUserLongMessage=Nije moguće unazaditi ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Podešavanja
|
||||
settings.update=Dostupno ažuriranje
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Verzija aplikacije:
|
||||
settings.downloadOption.title=Odaberite opciju preuzimanja (Za preuzimanje pojedinačnih fajlova bez zip formata):
|
||||
settings.downloadOption.1=Otvori u istom prozoru
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Preuzmi fajl
|
||||
settings.zipThreshold=Zipuj fajlove kada pređe broj preuzetih fajlova
|
||||
settings.signOut=Odjava
|
||||
settings.accountSettings=Podešavanja naloga
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Promeni pristupne podatke
|
||||
changeCreds.header=Ažurirajte detalje svog naloga
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo korisnik (Bez prilagođenih podešavanja)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi
|
||||
adminUserSettings.submit=Sačuvaj korisnika
|
||||
adminUserSettings.changeUserRole=Promenite ulogu korisnika
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Zapamti me
|
||||
login.invalid=Neispravno korisničko ime ili lozinka.
|
||||
login.locked=Vaš nalog je zaključan.
|
||||
login.signinTitle=Molimo vas da se prijavite
|
||||
login.ssoSignIn=Prijavite se putem jedinstvene prijave
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF u PDF/A
|
||||
pdfToPDFA.credit=Ova usluga koristi OCRmyPDF za konverziju u PDF/A format
|
||||
pdfToPDFA.submit=Konvertuj
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Unesite broj vertikalnih podele
|
||||
split-by-sections.submit=Razdvoji PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=New Username already exists.
|
||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||
downgradeCurrentUserMessage=Kan inte nedgradera nuvarande användares roll
|
||||
downgradeCurrentUserLongMessage=Kan inte nedgradera nuvarande användares roll. Därför kommer den aktuella användaren inte att visas.
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Inställningar
|
||||
settings.update=Uppdatering tillgänglig
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=Appversion:
|
||||
settings.downloadOption.title=Välj nedladdningsalternativ (för nedladdning av en fil utan zip):
|
||||
settings.downloadOption.1=Öppnas i samma fönster
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Ladda ner fil
|
||||
settings.zipThreshold=Zip-filer när antalet nedladdade filer överskrider
|
||||
settings.signOut=Sign Out
|
||||
settings.accountSettings=Account Settings
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=Change Credentials
|
||||
changeCreds.header=Update Your Account Details
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
adminUserSettings.submit=Save User
|
||||
adminUserSettings.changeUserRole=Ändra användarens roll
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Remember me
|
||||
login.invalid=Invalid username or password.
|
||||
login.locked=Your account has been locked.
|
||||
login.signinTitle=Please sign in
|
||||
login.ssoSignIn=Logga in via enkel inloggning
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User inaktiverad
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF till PDF/A
|
||||
pdfToPDFA.credit=Denna tjänst använder OCRmyPDF för PDF/A-konvertering
|
||||
pdfToPDFA.submit=Konvertera
|
||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.merge=Merge Into One PDF
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Licenses
|
||||
licenses.title=3rd Party Licenses
|
||||
|
||||
@@ -57,6 +57,8 @@ usernameExistsMessage=Yeni Kullanıcı Adı zaten var.
|
||||
invalidUsernameMessage=Geçersiz kullanıcı adı, Kullanıcı adı yalnızca alfabe karakterleri ve sayılar içermelidir.
|
||||
deleteCurrentUserMessage=Şu anda oturum açmış olan kullanıcı silinemiyor.
|
||||
deleteUsernameExistsMessage=Kullanıcı adı mevcut değil ve silinemez.
|
||||
downgradeCurrentUserMessage=Mevcut kullanıcının rolü düşürülemiyor
|
||||
downgradeCurrentUserLongMessage=Mevcut kullanıcının rolü düşürülemiyor. Bu nedenle, mevcut kullanıcı gösterilmeyecektir.
|
||||
error=Hata
|
||||
oops=Tüh!
|
||||
help=Yardım
|
||||
@@ -66,32 +68,32 @@ seeDockerHub=Docker Hub'a bakın
|
||||
visitGithub=Github Deposunu Ziyaret Edin
|
||||
donate=Bağış Yapın
|
||||
color=Renk
|
||||
sponsor=Sponsor
|
||||
info=Info
|
||||
sponsor=Bağış
|
||||
|
||||
|
||||
|
||||
###############
|
||||
# Pipeline #
|
||||
###############
|
||||
pipeline.header=Pipeline Menü (Beta)
|
||||
pipeline.header=Çoklu İşlemler Menü (Beta)
|
||||
pipeline.uploadButton=Upload edin
|
||||
pipeline.configureButton=Yapılandır
|
||||
pipeline.defaultOption=Özel
|
||||
pipeline.submitButton=Gönder
|
||||
pipeline.help=Pipeline Yardım
|
||||
pipeline.help=Çoklu İşlemler Yardım
|
||||
pipeline.scanHelp=Klasör Tarama Yardımı
|
||||
|
||||
######################
|
||||
# Pipeline Options #
|
||||
######################
|
||||
pipelineOptions.header=Pipeline Yapılandırma
|
||||
pipelineOptions.pipelineNameLabel=Pipeline İsim
|
||||
pipelineOptions.header=Çoklu İşlemler Yapılandırma
|
||||
pipelineOptions.pipelineNameLabel=Çoklu İşlemler İsim
|
||||
pipelineOptions.saveSettings=Ayarları Kaydet
|
||||
pipelineOptions.pipelineNamePrompt=Buraya isim girin
|
||||
pipelineOptions.selectOperation=İşlem Seçin
|
||||
pipelineOptions.addOperationButton=İşlem ekle
|
||||
pipelineOptions.pipelineHeader=Pipeline:
|
||||
pipelineOptions.pipelineHeader=Çoklu İşlemler:
|
||||
pipelineOptions.saveButton=İndir
|
||||
pipelineOptions.validateButton=Doğrula
|
||||
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=Ayarlar
|
||||
settings.update=Güncelleme mevcut
|
||||
settings.updateAvailable={0} mevcut kurulu sürümdür. Yeni bir sürüm ({1}) mevcuttur.
|
||||
settings.appVersion=Uygulama Sürümü:
|
||||
settings.downloadOption.title=İndirme seçeneği seçin (Zip olmayan tek dosya indirmeler için):
|
||||
settings.downloadOption.1=Aynı pencerede aç
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=Dosyayı indir
|
||||
settings.zipThreshold=İndirilen dosya sayısı şu değeri aştığında zip dosyası oluştur:
|
||||
settings.signOut=Oturumu Kapat
|
||||
settings.accountSettings=Hesap Ayarları
|
||||
|
||||
|
||||
settings.bored.help=Paskalya yumurtası oyunu etkinleştirir
|
||||
settings.cacheInputs.name=Form girdilerini kaydet
|
||||
settings.cacheInputs.help=Gelecekteki çalıştırmalar için önceden kullanılan girdileri saklamayı etkinleştirin
|
||||
|
||||
changeCreds.title=Giriş Bilgilerini Değiştir
|
||||
changeCreds.header=Hesap Detaylarınızı Güncelleyin
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=Demo Kullanıcısı (Özel ayar yok)
|
||||
adminUserSettings.internalApiUser=Dahili API Kullanıcısı
|
||||
adminUserSettings.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla
|
||||
adminUserSettings.submit=Kullanıcıyı Kaydet
|
||||
adminUserSettings.changeUserRole=Kullanıcı rolünü değiştir
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -330,8 +335,8 @@ 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=Hattı (İleri Seviye)
|
||||
home.pipeline.desc=Hattı betikleri tanımlayarak PDF'lere birden fazla işlemi çalıştır
|
||||
home.pipeline.title=Çoklu İşlemler (İleri Seviye)
|
||||
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
|
||||
|
||||
home.add-page-numbers.title=Sayfa Numaraları Ekle
|
||||
@@ -440,6 +445,8 @@ login.rememberme=Beni hatırla
|
||||
login.invalid=Geçersiz kullanıcı adı veya şifre.
|
||||
login.locked=Hesabınız kilitlendi.
|
||||
login.signinTitle=Lütfen giriş yapınız.
|
||||
login.ssoSignIn=Tek Oturum Açma ile Giriş Yap
|
||||
login.oauth2AutoCreateDisabled=OAUTH2 Otomatik Oluşturma Kullanıcı Devre Dışı Bırakıldı
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -600,7 +607,7 @@ autoSplitPDF.submit=Gönder
|
||||
|
||||
|
||||
#pipeline
|
||||
pipeline.title=Pipeline
|
||||
pipeline.title=Çoklu İşlemler
|
||||
|
||||
|
||||
#pageLayout
|
||||
@@ -669,7 +676,7 @@ BookToPDF.submit=Dönüştür
|
||||
#PDFToBook
|
||||
PDFToBook.title=PDF'den Kitaba
|
||||
PDFToBook.header=PDF'den Kitaba
|
||||
PDFToBook.selectText.1=Format
|
||||
PDFToBook.selectText.1=Format biçimi
|
||||
PDFToBook.credit=Kalibre Kullanır
|
||||
PDFToBook.submit=Dönüştür
|
||||
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF'den PDF/A'ya
|
||||
pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için OCRmyPDF kullanır
|
||||
pdfToPDFA.submit=Dönüştür
|
||||
pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor
|
||||
pdfToPDFA.outputFormat=Çıkış formatı
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=Dikey bölme sayısını girin
|
||||
split-by-sections.submit=PDF'yi Böl
|
||||
split-by-sections.merge=Bir PDF'de Birleştirin
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Dosya Yazdır
|
||||
printFile.header=Dosyayı Yazıcıya Yazdır
|
||||
printFile.selectText.1=Yazdırılacak Dosyayı Seçin
|
||||
printFile.selectText.2=Yazıcı Adını Girin
|
||||
printFile.submit=Yazdır
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=Lisanslar
|
||||
licenses.title=3. Taraf Lisansları
|
||||
|
||||
1063
src/main/resources/messages_uk_UA.properties
Normal file
1063
src/main/resources/messages_uk_UA.properties
Normal file
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,8 @@ usernameExistsMessage=新用户名已存在。
|
||||
invalidUsernameMessage=用户名无效,用户名只能由字母字符和数字组成。
|
||||
deleteCurrentUserMessage=无法删除当前登录的用户。
|
||||
deleteUsernameExistsMessage=用户名不存在,无法删除。
|
||||
downgradeCurrentUserMessage=无法降级当前用户的角色
|
||||
downgradeCurrentUserLongMessage=无法降级当前用户的角色。因此,当前用户将不会显示。
|
||||
error=错误
|
||||
oops=哎呀!
|
||||
help=帮助
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=设置
|
||||
settings.update=可更新
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=应用程序版本:
|
||||
settings.downloadOption.title=选择下载选项(单个文件非压缩文件):
|
||||
settings.downloadOption.1=在同一窗口打开
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=下载文件
|
||||
settings.zipThreshold=当下载的文件数量超过限制时,将文件压缩。
|
||||
settings.signOut=登出
|
||||
settings.accountSettings=帐号设定
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=更改凭证
|
||||
changeCreds.header=更新您的账户详情
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=演示用户(无自定义设置)
|
||||
adminUserSettings.internalApiUser=内部API用户
|
||||
adminUserSettings.forceChange=强制用户在登录时更改用户名/密码
|
||||
adminUserSettings.submit=保存用户
|
||||
adminUserSettings.changeUserRole=更改用户角色
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=记住我
|
||||
login.invalid=用户名或密码无效。
|
||||
login.locked=您的账户已被锁定。
|
||||
login.signinTitle=请登录
|
||||
login.ssoSignIn=通过单点登录登录
|
||||
login.oauth2AutoCreateDisabled=OAUTH2自动创建用户已禁用
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF转换为PDF/A
|
||||
pdfToPDFA.credit=此服务使用OCRmyPDF进行PDF/A转换
|
||||
pdfToPDFA.submit=转换
|
||||
pdfToPDFA.tip=目前不支持上传多个
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=输入垂直分割数
|
||||
split-by-sections.submit=分割PDF
|
||||
split-by-sections.merge=是否合并为一个pdf
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=许可证
|
||||
licenses.title=第三方许可证
|
||||
|
||||
@@ -57,17 +57,19 @@ usernameExistsMessage=新使用者名稱已存在。
|
||||
invalidUsernameMessage=使用者名無效,使用者名只能包含字母字元和數位。
|
||||
deleteCurrentUserMessage=無法刪除目前登錄的使用者。
|
||||
deleteUsernameExistsMessage=使用者名不存在,無法刪除。
|
||||
error=Error
|
||||
oops=Oops!
|
||||
help=Help
|
||||
goHomepage=Go to Homepage
|
||||
joinDiscord=Join our Discord server
|
||||
seeDockerHub=See Docker Hub
|
||||
visitGithub=Visit Github Repository
|
||||
donate=Donate
|
||||
color=Color
|
||||
sponsor=Sponsor
|
||||
info=Info
|
||||
downgradeCurrentUserMessage=無法降級目前使用者的角色
|
||||
downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。
|
||||
error=錯誤
|
||||
oops=哎呀!
|
||||
help=幫助
|
||||
goHomepage=前往首頁
|
||||
joinDiscord=加入我們的Discord服務器
|
||||
seeDockerHub=查看Docker Hub
|
||||
visitGithub=訪問Github存儲庫
|
||||
donate=捐贈
|
||||
color=顏色
|
||||
sponsor=贊助
|
||||
|
||||
|
||||
|
||||
@@ -117,6 +119,7 @@ navbar.sections.edit=View & Edit
|
||||
#############
|
||||
settings.title=設定
|
||||
settings.update=有更新可用
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.appVersion=應用版本:
|
||||
settings.downloadOption.title=選擇下載選項(對於單一檔案非壓縮下載):
|
||||
settings.downloadOption.1=在同一視窗中開啟
|
||||
@@ -125,8 +128,9 @@ settings.downloadOption.3=下載檔案
|
||||
settings.zipThreshold=當下載的檔案數量超過時,壓縮檔案
|
||||
settings.signOut=登出
|
||||
settings.accountSettings=帳戶設定
|
||||
|
||||
|
||||
settings.bored.help=Enables easter egg game
|
||||
settings.cacheInputs.name=Save form inputs
|
||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
||||
|
||||
changeCreds.title=變更憑證
|
||||
changeCreds.header=更新您的帳戶詳細資訊
|
||||
@@ -176,6 +180,7 @@ adminUserSettings.demoUser=示範用途的使用者(無自訂設定)
|
||||
adminUserSettings.internalApiUser=內部 API 使用者
|
||||
adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼
|
||||
adminUserSettings.submit=儲存
|
||||
adminUserSettings.changeUserRole=更改使用者身份
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -440,6 +445,8 @@ login.rememberme=記住我
|
||||
login.invalid=使用者名稱或密碼無效。
|
||||
login.locked=您的帳戶已被鎖定。
|
||||
login.signinTitle=請登入
|
||||
login.ssoSignIn=透過織網單一簽入
|
||||
login.oauth2AutoCreateDisabled=OAUTH2自動建立使用者已停用
|
||||
|
||||
|
||||
#auto-redact
|
||||
@@ -945,6 +952,7 @@ pdfToPDFA.header=PDF 轉 PDF/A
|
||||
pdfToPDFA.credit=此服務使用 OCRmyPDF 進行 PDF/A 轉換
|
||||
pdfToPDFA.submit=轉換
|
||||
pdfToPDFA.tip=目前不支援上傳多個
|
||||
pdfToPDFA.outputFormat=Output format
|
||||
|
||||
|
||||
#PDFToWord
|
||||
@@ -1028,6 +1036,15 @@ split-by-sections.vertical.placeholder=輸入垂直劃分的數量
|
||||
split-by-sections.submit=分割 PDF
|
||||
split-by-sections.merge=是否合併為一個pdf
|
||||
|
||||
|
||||
#printFile
|
||||
printFile.title=Print File
|
||||
printFile.header=Print File to Printer
|
||||
printFile.selectText.1=Select File to Print
|
||||
printFile.selectText.2=Enter Printer Name
|
||||
printFile.submit=Print
|
||||
|
||||
|
||||
#licenses
|
||||
licenses.nav=許可證
|
||||
licenses.title=第三方許可證
|
||||
@@ -1038,15 +1055,15 @@ licenses.license=許可證
|
||||
|
||||
|
||||
# error
|
||||
error.sorry=Sorry for the issue!
|
||||
error.needHelp=Need help / Found an issue?
|
||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
||||
error.404.1=We can't seem to find the page you're looking for.
|
||||
error.404.2=Something went wrong
|
||||
error.github=Submit a ticket on GitHub
|
||||
error.showStack=Show Stack Trace
|
||||
error.copyStack=Copy Stack Trace
|
||||
error.githubSubmit=GitHub - Submit a ticket
|
||||
error.discordSubmit=Discord - Submit Support post
|
||||
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 - 提交支援帖子
|
||||
|
||||
|
||||
@@ -7,16 +7,24 @@ security:
|
||||
csrfDisabled: true
|
||||
loginAttemptCount: 5 # lock user account after 5 tries
|
||||
loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts
|
||||
#oauth2:
|
||||
# enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
|
||||
# issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point
|
||||
# clientId: "" # Client ID from your provider
|
||||
# clientSecret: "" # Client Secret from your provider
|
||||
# autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
|
||||
|
||||
system:
|
||||
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
||||
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow
|
||||
enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes)
|
||||
showUpdate: true # see when a new update is available
|
||||
showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
|
||||
|
||||
#ui:
|
||||
# appName: exampleAppName # Application's visible name
|
||||
# homeDescription: I am a description # Short description or tagline shown on homepage.
|
||||
# appNameNavbar: navbarName # Name displayed on the navigation bar
|
||||
ui:
|
||||
appName: null # Application's visible name
|
||||
homeDescription: null # Short description or tagline shown on homepage.
|
||||
appNameNavbar: null # Name displayed on the navigation bar
|
||||
|
||||
endpoints:
|
||||
toRemove: [] # List endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])
|
||||
@@ -24,3 +32,7 @@ endpoints:
|
||||
|
||||
metrics:
|
||||
enabled: true # 'true' to enable Info APIs (`/api/*`) endpoints, 'false' to disable
|
||||
|
||||
# Automatically Generated Settings (Do Not Edit Directly)
|
||||
AutomaticallyGenerated:
|
||||
key: example
|
||||
@@ -81,6 +81,13 @@
|
||||
"moduleLicense": "GNU Lesser General Public License v3 (LGPL-v3)",
|
||||
"moduleLicenseUrl": "http://www.gnu.org/licenses/lgpl-3.0.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.github.stephenc.jcip:jcip-annotations",
|
||||
"moduleUrl": "http://stephenc.github.com/jcip-annotations",
|
||||
"moduleVersion": "1.0-1",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.github.vladimir-bukhtoyarov:bucket4j-core",
|
||||
"moduleUrl": "http://github.com/vladimir-bukhtoyarov/bucket4j/bucket4j-core",
|
||||
@@ -109,6 +116,34 @@
|
||||
"moduleLicense": "LGPL",
|
||||
"moduleLicenseUrl": "http://www.martiansoftware.com/jsap/license.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.nimbusds:content-type",
|
||||
"moduleUrl": "https://connect2id.com",
|
||||
"moduleVersion": "2.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.nimbusds:lang-tag",
|
||||
"moduleUrl": "https://connect2id.com/",
|
||||
"moduleVersion": "1.7",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.nimbusds:nimbus-jose-jwt",
|
||||
"moduleUrl": "https://connect2id.com",
|
||||
"moduleVersion": "9.24.4",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.nimbusds:oauth2-oidc-sdk",
|
||||
"moduleUrl": "https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions",
|
||||
"moduleVersion": "9.43.3",
|
||||
"moduleLicense": "Apache License, version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.opencsv:opencsv",
|
||||
"moduleUrl": "http://opencsv.sf.net",
|
||||
@@ -335,6 +370,20 @@
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "net.minidev:accessors-smart",
|
||||
"moduleUrl": "https://urielch.github.io/",
|
||||
"moduleVersion": "2.5.0",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "net.minidev:json-smart",
|
||||
"moduleUrl": "https://urielch.github.io/",
|
||||
"moduleVersion": "2.5.0",
|
||||
"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/",
|
||||
@@ -547,6 +596,13 @@
|
||||
"moduleLicense": "Public Domain, per Creative Commons CC0",
|
||||
"moduleLicenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.ow2.asm:asm",
|
||||
"moduleUrl": "http://asm.ow2.org",
|
||||
"moduleVersion": "9.3",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.slf4j:jul-to-slf4j",
|
||||
"moduleUrl": "http://www.slf4j.org",
|
||||
@@ -663,6 +719,13 @@
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-oauth2-client",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.2.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-security",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
@@ -726,6 +789,27 @@
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-oauth2-client",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.2.3",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-oauth2-core",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.2.3",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-oauth2-jose",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.2.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",
|
||||
|
||||
@@ -87,4 +87,43 @@
|
||||
.jumbotron {
|
||||
padding: 3rem 3rem;
|
||||
/* Reduce vertical padding */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.lookatme {
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.lookatme::after {
|
||||
color: #e33100;
|
||||
text-shadow: 0 0 5px #e33100;
|
||||
/* in the html, the data-lookatme-text attribute must */
|
||||
/* contain the same text as the .lookatme element */
|
||||
content: attr(data-lookatme-text);
|
||||
padding: inherit;
|
||||
position: absolute;
|
||||
inset: 0 0 0 0;
|
||||
z-index: 1;
|
||||
/* 20 steps / 2 seconds = 10fps */
|
||||
-webkit-animation: 2s infinite Pulse steps(20);
|
||||
animation: 2s infinite Pulse steps(20);
|
||||
}
|
||||
|
||||
@keyframes Pulse {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
src/main/resources/static/images/flags/ua.svg
Normal file
6
src/main/resources/static/images/flags/ua.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ua" viewBox="0 0 640 480">
|
||||
<g fill-rule="evenodd" stroke-width="1pt">
|
||||
<path fill="gold" d="M0 0h640v480H0z"/>
|
||||
<path fill="#0057b8" d="M0 0h640v240H0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 232 B |
1
src/main/resources/static/images/update.svg
Normal file
1
src/main/resources/static/images/update.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21,10.12H14.22L16.96,7.3C14.23,4.6 9.81,4.5 7.08,7.2C4.35,9.91 4.35,14.28 7.08,17C9.81,19.7 14.23,19.7 16.96,17C18.32,15.65 19,14.08 19,12.1H21C21,14.08 20.12,16.65 18.36,18.39C14.85,21.87 9.15,21.87 5.64,18.39C2.14,14.92 2.11,9.28 5.62,5.81C9.13,2.34 14.76,2.34 18.27,5.81L21,3V10.12M12.5,8V12.25L16,14.33L15.28,15.54L11,13V8H12.5Z" /></svg>
|
||||
|
After Width: | Height: | Size: 412 B |
82
src/main/resources/static/js/cacheFormInputs.js
Normal file
82
src/main/resources/static/js/cacheFormInputs.js
Normal file
@@ -0,0 +1,82 @@
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
var cacheInputs = localStorage.getItem("cacheInputs") || "disabled";
|
||||
if (cacheInputs !== "enabled") {
|
||||
return; // Stop execution if caching is not enabled
|
||||
}
|
||||
|
||||
// Function to generate a key based on the form's action attribute
|
||||
function generateStorageKey(form) {
|
||||
const action = form.getAttribute('action');
|
||||
if (!action || action.length < 3) {
|
||||
return null; // Not a valid action, return null to skip processing
|
||||
}
|
||||
return 'formData_' + encodeURIComponent(action);
|
||||
}
|
||||
|
||||
// Function to save form data to localStorage
|
||||
function saveFormData(form) {
|
||||
const formKey = generateStorageKey(form);
|
||||
if (!formKey) return; // Skip if no valid key
|
||||
|
||||
const formData = {};
|
||||
const elements = form.elements;
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i];
|
||||
// Skip elements without names, passwords, files, hidden fields, and submit/reset buttons
|
||||
if (!element.name ||
|
||||
element.type === 'password' ||
|
||||
element.type === 'file' ||
|
||||
//element.type === 'hidden' ||
|
||||
element.type === 'submit' ||
|
||||
element.type === 'reset') {
|
||||
continue;
|
||||
}
|
||||
// Handle checkboxes: store only if checked
|
||||
if (element.type === 'checkbox') {
|
||||
if (element.checked) {
|
||||
formData[element.name] = element.value;
|
||||
} else {
|
||||
continue; // Skip unchecked boxes
|
||||
}
|
||||
} else {
|
||||
// Skip saving empty values
|
||||
if (element.value === "" || element.value == null) {
|
||||
continue;
|
||||
}
|
||||
formData[element.name] = element.value;
|
||||
}
|
||||
}
|
||||
localStorage.setItem(formKey, JSON.stringify(formData));
|
||||
}
|
||||
|
||||
// Function to load form data from localStorage
|
||||
function loadFormData(form) {
|
||||
const formKey = generateStorageKey(form);
|
||||
if (!formKey) return; // Skip if no valid key
|
||||
|
||||
const savedData = localStorage.getItem(formKey);
|
||||
if (savedData) {
|
||||
const formData = JSON.parse(savedData);
|
||||
for (const key in formData) {
|
||||
if (formData.hasOwnProperty(key) && form.elements[key]) {
|
||||
const element = form.elements[key];
|
||||
if (element.type === 'checkbox') {
|
||||
element.checked = true;
|
||||
} else {
|
||||
element.value = formData[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach event listeners and load data for all forms
|
||||
const forms = document.querySelectorAll('form');
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('submit', function(event) {
|
||||
saveFormData(form);
|
||||
});
|
||||
loadFormData(form);
|
||||
});
|
||||
});
|
||||
@@ -5,74 +5,130 @@ const DraggableUtils = {
|
||||
pdfDoc: null,
|
||||
pageIndex: 0,
|
||||
documentsMap: new Map(),
|
||||
lastInteracted: null,
|
||||
|
||||
init() {
|
||||
interact(".draggable-canvas")
|
||||
.draggable({
|
||||
listeners: {
|
||||
move: (event) => {
|
||||
const target = event.target;
|
||||
const x = (parseFloat(target.getAttribute("data-bs-x")) || 0) + event.dx;
|
||||
const y = (parseFloat(target.getAttribute("data-bs-y")) || 0) + event.dy;
|
||||
.draggable({
|
||||
listeners: {
|
||||
move: (event) => {
|
||||
const target = event.target;
|
||||
const x = (parseFloat(target.getAttribute("data-bs-x")) || 0)
|
||||
+ event.dx;
|
||||
const y = (parseFloat(target.getAttribute("data-bs-y")) || 0)
|
||||
+ event.dy;
|
||||
|
||||
target.style.transform = `translate(${x}px, ${y}px)`;
|
||||
target.setAttribute("data-bs-x", x);
|
||||
target.setAttribute("data-bs-y", y);
|
||||
target.style.transform = `translate(${x}px, ${y}px)`;
|
||||
target.setAttribute("data-bs-x", x);
|
||||
target.setAttribute("data-bs-y", y);
|
||||
|
||||
this.onInteraction(target);
|
||||
},
|
||||
this.onInteraction(target);
|
||||
//update the last interacted element
|
||||
this.lastInteracted = event.target;
|
||||
},
|
||||
})
|
||||
.resizable({
|
||||
edges: { left: true, right: true, bottom: true, top: true },
|
||||
listeners: {
|
||||
move: (event) => {
|
||||
var target = event.target;
|
||||
var x = parseFloat(target.getAttribute("data-bs-x")) || 0;
|
||||
var y = parseFloat(target.getAttribute("data-bs-y")) || 0;
|
||||
},
|
||||
})
|
||||
.resizable({
|
||||
edges: { left: true, right: true, bottom: true, top: true },
|
||||
listeners: {
|
||||
move: (event) => {
|
||||
var target = event.target;
|
||||
var x = parseFloat(target.getAttribute("data-bs-x")) || 0;
|
||||
var y = parseFloat(target.getAttribute("data-bs-y")) || 0;
|
||||
|
||||
// check if control key is pressed
|
||||
if (event.ctrlKey) {
|
||||
const aspectRatio = target.offsetWidth / target.offsetHeight;
|
||||
// preserve aspect ratio
|
||||
let width = event.rect.width;
|
||||
let height = event.rect.height;
|
||||
// check if control key is pressed
|
||||
if (event.ctrlKey) {
|
||||
const aspectRatio = target.offsetWidth / target.offsetHeight;
|
||||
// preserve aspect ratio
|
||||
let width = event.rect.width;
|
||||
let height = event.rect.height;
|
||||
|
||||
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
|
||||
event.rect.width = width;
|
||||
event.rect.height = height;
|
||||
if (Math.abs(event.deltaRect.width) >= Math.abs(
|
||||
event.deltaRect.height)) {
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
|
||||
target.style.width = event.rect.width + "px";
|
||||
target.style.height = event.rect.height + "px";
|
||||
event.rect.width = width;
|
||||
event.rect.height = height;
|
||||
}
|
||||
|
||||
// translate when resizing from top or left edges
|
||||
x += event.deltaRect.left;
|
||||
y += event.deltaRect.top;
|
||||
target.style.width = event.rect.width + "px";
|
||||
target.style.height = event.rect.height + "px";
|
||||
|
||||
target.style.transform = "translate(" + x + "px," + y + "px)";
|
||||
// translate when resizing from top or left edges
|
||||
x += event.deltaRect.left;
|
||||
y += event.deltaRect.top;
|
||||
|
||||
target.setAttribute("data-bs-x", x);
|
||||
target.setAttribute("data-bs-y", y);
|
||||
target.textContent = Math.round(event.rect.width) + "\u00D7" + Math.round(event.rect.height);
|
||||
target.style.transform = "translate(" + x + "px," + y + "px)";
|
||||
|
||||
this.onInteraction(target);
|
||||
},
|
||||
target.setAttribute("data-bs-x", x);
|
||||
target.setAttribute("data-bs-y", y);
|
||||
target.textContent = Math.round(event.rect.width) + "\u00D7"
|
||||
+ Math.round(event.rect.height);
|
||||
|
||||
this.onInteraction(target);
|
||||
},
|
||||
},
|
||||
|
||||
modifiers: [
|
||||
interact.modifiers.restrictSize({
|
||||
min: { width: 5, height: 5 },
|
||||
}),
|
||||
],
|
||||
inertia: true,
|
||||
modifiers: [
|
||||
interact.modifiers.restrictSize({
|
||||
min: {width: 5, height: 5},
|
||||
}),
|
||||
],
|
||||
inertia: true,
|
||||
});
|
||||
//Arrow key Support for Add-Image and Sign pages
|
||||
if(window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) {
|
||||
window.addEventListener('keydown', (event) => {
|
||||
//Check for last interacted element
|
||||
if (!this.lastInteracted){
|
||||
return;
|
||||
}
|
||||
// Get the currently selected element
|
||||
const target = this.lastInteracted;
|
||||
|
||||
// Step size relatively to the elements size
|
||||
const stepX = target.offsetWidth * 0.05;
|
||||
const stepY = target.offsetHeight * 0.05;
|
||||
|
||||
// Get the current x and y coordinates
|
||||
let x = (parseFloat(target.getAttribute('data-bs-x')) || 0);
|
||||
let y = (parseFloat(target.getAttribute('data-bs-y')) || 0);
|
||||
|
||||
// Check which key was pressed and update the coordinates accordingly
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
y -= stepY;
|
||||
event.preventDefault(); // Prevent the default action
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
y += stepY;
|
||||
event.preventDefault();
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
x -= stepX;
|
||||
event.preventDefault();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
x += stepX;
|
||||
event.preventDefault();
|
||||
break;
|
||||
default:
|
||||
return; // Listen only to arrow keys
|
||||
}
|
||||
|
||||
// Update position
|
||||
target.style.transform = `translate(${x}px, ${y}px)`;
|
||||
target.setAttribute('data-bs-x', x);
|
||||
target.setAttribute('data-bs-y', y);
|
||||
|
||||
DraggableUtils.onInteraction(target);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onInteraction(target) {
|
||||
this.boxDragContainer.appendChild(target);
|
||||
},
|
||||
@@ -88,9 +144,18 @@ const DraggableUtils = {
|
||||
createdCanvas.setAttribute("data-bs-x", x);
|
||||
createdCanvas.setAttribute("data-bs-y", y);
|
||||
|
||||
//Click element in order to enable arrow keys
|
||||
createdCanvas.addEventListener('click', () => {
|
||||
this.lastInteracted = createdCanvas;
|
||||
});
|
||||
|
||||
createdCanvas.onclick = (e) => this.onInteraction(e.target);
|
||||
|
||||
this.boxDragContainer.appendChild(createdCanvas);
|
||||
|
||||
//Enable Arrow keys directly after the element is created
|
||||
this.lastInteracted = createdCanvas;
|
||||
|
||||
return createdCanvas;
|
||||
},
|
||||
createDraggableCanvasFromUrl(dataUrl) {
|
||||
@@ -134,6 +199,11 @@ const DraggableUtils = {
|
||||
},
|
||||
deleteDraggableCanvas(element) {
|
||||
if (element) {
|
||||
//Check if deleted element is the last interacted
|
||||
if (this.lastInteracted === element) {
|
||||
// If it is, set lastInteracted to null
|
||||
this.lastInteracted = null;
|
||||
}
|
||||
element.remove();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -104,4 +104,9 @@ function setupFileInput(chooser) {
|
||||
$(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt);
|
||||
}
|
||||
}
|
||||
//Listen for event of file being removed and the filter it out of the allFiles array
|
||||
document.addEventListener("fileRemoved", function (e) {
|
||||
const fileName = e.detail;
|
||||
allFiles = allFiles.filter(file => file.name !== fileName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,19 +30,39 @@ async function getLatestReleaseVersion() {
|
||||
|
||||
async function checkForUpdate() {
|
||||
// Initialize the update button as hidden
|
||||
var updateBtn = document.getElementById("update-btn");
|
||||
var updateBtn = document.getElementById("update-btn") || null;
|
||||
var updateLink = document.getElementById("update-link") || null;
|
||||
if (updateBtn !== null) {
|
||||
updateBtn.style.display = "none";
|
||||
}
|
||||
if (updateLink !== null) {
|
||||
console.log("hidden!");
|
||||
if (!updateLink.classList.contains("visually-hidden")) {
|
||||
updateLink.classList.add("visually-hidden");
|
||||
}
|
||||
}
|
||||
|
||||
const latestVersion = await getLatestReleaseVersion();
|
||||
console.log("latestVersion=" + latestVersion);
|
||||
console.log("currentVersion=" + currentVersion);
|
||||
console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion));
|
||||
if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
|
||||
document.getElementById("update-btn").style.display = "block";
|
||||
if (updateBtn != null) {
|
||||
document.getElementById("update-btn").style.display = "block";
|
||||
}
|
||||
if (updateLink !== null) {
|
||||
document.getElementById("app-update").innerHTML = updateAvailable.replace("{0}", '<b>' + currentVersion + '</b>').replace("{1}", '<b>' + latestVersion + '</b>');
|
||||
if (updateLink.classList.contains("visually-hidden")) {
|
||||
updateLink.classList.remove("visually-hidden");
|
||||
}
|
||||
}
|
||||
console.log("visible");
|
||||
} else {
|
||||
if (updateLink !== null) {
|
||||
if (!updateLink.classList.contains("visually-hidden")) {
|
||||
updateLink.classList.add("visually-hidden");
|
||||
}
|
||||
}
|
||||
console.log("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,12 @@ function reorderCards() {
|
||||
cards.sort(function (a, b) {
|
||||
var aIsFavorite = localStorage.getItem(a.id) === "favorite";
|
||||
var bIsFavorite = localStorage.getItem(b.id) === "favorite";
|
||||
if (a.id === "update-link") {
|
||||
return -1;
|
||||
}
|
||||
if (b.id === "update-link") {
|
||||
return 1;
|
||||
}
|
||||
if (aIsFavorite && !bIsFavorite) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -69,8 +69,13 @@ function attachMoveButtons() {
|
||||
removeButtons[i].addEventListener("click", function (event) {
|
||||
event.preventDefault();
|
||||
var parent = this.closest(".list-group-item");
|
||||
//Get name of removed file
|
||||
var fileName = parent.querySelector(".filename").innerText;
|
||||
parent.remove();
|
||||
updateFiles();
|
||||
//Dispatch a custom event with the name of the removed file
|
||||
var event = new CustomEvent("fileRemoved", { detail: fileName });
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,3 +31,12 @@ document.getElementById("boredWaiting").addEventListener("change", function () {
|
||||
boredWaiting = this.checked ? "enabled" : "disabled";
|
||||
localStorage.setItem("boredWaiting", boredWaiting);
|
||||
});
|
||||
|
||||
var cacheInputs = localStorage.getItem("cacheInputs") || "disabled";
|
||||
document.getElementById("cacheInputs").checked = cacheInputs === "enabled";
|
||||
|
||||
document.getElementById("cacheInputs").addEventListener("change", function () {
|
||||
cacheInputs = this.checked ? "enabled" : "disabled";
|
||||
localStorage.setItem("cacheInputs", cacheInputs);
|
||||
});
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</th:block>
|
||||
<!-- Change Username Form -->
|
||||
<h4 th:text="#{account.changeUsername}">Change Username?</h4>
|
||||
<form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-username" method="post">
|
||||
<form th:if="${!oAuth2Login}" id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-username" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="newUsername" th:text="#{account.newUsername}">Change Username</label>
|
||||
<input type="text" class="form-control" name="newUsername" id="newUsername"
|
||||
@@ -62,7 +62,7 @@
|
||||
</form>
|
||||
|
||||
<!-- Change Password Form -->
|
||||
<h4 th:text="#{account.changePassword}">Change Password?</h4>
|
||||
<h4 th:if="${!oAuth2Login}" th:text="#{account.changePassword}">Change Password?</h4>
|
||||
<form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-password" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="currentPassword" th:text="#{account.oldPassword}">Old Password</label>
|
||||
@@ -85,6 +85,7 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<!-- API Key Form -->
|
||||
<h4 th:text="#{account.yourApiKey}">API Key</h4>
|
||||
<div class="card mt-4 mb-4">
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
||||
<select name="role" class="form-control" required>
|
||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -66,6 +67,32 @@
|
||||
<label class="form-check-label" for="forceChange" th:text="#{adminUserSettings.forceChange}">Force user to change username/password on login</label>
|
||||
</div>
|
||||
|
||||
<!-- Add other fields as required -->
|
||||
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
||||
</form>
|
||||
<hr />
|
||||
<h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2>
|
||||
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button>
|
||||
<div th:if="${param.messageType != null and param.messageType.size() > 0 and (param.messageType[0] == 'userNotFound' or param.messageType[0] == 'downgradeCurrentUser')}" class="alert alert-danger">
|
||||
<span th:if="${param.messageType[0] == 'userNotFound'}" th:text="#{userNotFoundMessage}">Username not found</span>
|
||||
<span th:if="${param.messageType[0] == 'downgradeCurrentUser'}" th:text="#{downgradeCurrentUserMessage}">Cannot downgrade current user's role</span>
|
||||
</div>
|
||||
<form action="/api/v1/user/admin/changeRole" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="username" th:text="#{username}">Username</label>
|
||||
<select name="username" class="form-control" required>
|
||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||
<option th:each="user : ${users}" th:if="${user.username != currentUsername}" th:value="${user.username}" th:text="${user.username}">Username</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
||||
<select name="role" class="form-control" required>
|
||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Add other fields as required -->
|
||||
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
||||
</form>
|
||||
@@ -73,6 +100,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script th:inline="javascript">
|
||||
$(document).ready(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
</script>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
<p th:text="#{pdfToPDFA.tip}"></p>
|
||||
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/pdfa}">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||
<div class="mb-3">
|
||||
<label th:text="#{pdfToPDFA.outputFormat}"></label>
|
||||
<select class="form-control" name="outputFormat">
|
||||
<option value="pdfa-1">PDF/A-1b</option>
|
||||
<option value="pdfa">PDF/A-2b</option>
|
||||
</select>
|
||||
</div>
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToPDFA.submit}"></button>
|
||||
</form>
|
||||
@@ -31,4 +38,4 @@
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -22,14 +22,13 @@
|
||||
<div class="mb-3">
|
||||
<label th:text="#{PDFToText.selectText.1}"></label>
|
||||
<select class="form-control" name="outputFormat">
|
||||
<option value="rtf">RTF</option>
|
||||
<option th:if="${@endpointConfiguration.isEndpointEnabled('pdf-to-rtf')}" value="rtf">RTF</option>
|
||||
<option value="txt">TXT</option>
|
||||
</select>
|
||||
</div>
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToText.submit}"></button>
|
||||
</form>
|
||||
<p class="mt-3" th:text="#{PDFToText.credit}"></p>
|
||||
<p th:if="${@endpointConfiguration.isEndpointEnabled('pdf-to-rtf')}" class="mt-3" th:text="#{PDFToText.credit}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<!-- Help Modal -->
|
||||
<link rel="stylesheet" href="css/errorBanner.css">
|
||||
|
||||
<script src="js/cacheFormInputs.js"></script>
|
||||
<script src="js/tab-container.js"></script>
|
||||
<script src="js/darkmode.js"></script>
|
||||
</th:block>
|
||||
|
||||
@@ -26,4 +26,5 @@
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hu_HU"> <img src="images/flags/hu.svg" alt="icon" width="20" height="15"> Hungarian</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hi_IN"> <img src="images/flags/in.svg" alt="icon" width="20" height="15"> हिन्दी</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sr_LATN_RS"> <img src="images/flags/rs.svg" alt="icon" width="20" height="15"> Srpski</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="uk_UA"> <img src="images/flags/ua.svg" alt="icon" width="20" height="15"> Українська</a>
|
||||
</th:block>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<script th:inline="javascript">
|
||||
const currentVersion = /*[[${@appVersion}]]*/ '';
|
||||
const noFavourites = /*[[#{noFavourites}]]*/ '';
|
||||
const updateAvailable = /*[[#{settings.updateAvailable}]]*/ '';
|
||||
</script>
|
||||
<script th:src="@{js/githubVersion.js}"></script>
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
@@ -367,7 +368,7 @@
|
||||
|
||||
<a href="swagger-ui/index.html" class="btn btn-sm btn-outline-primary mx-1" role="button"
|
||||
target="_blank">API</a>
|
||||
<a href="https://github.com/Stirling-Tools/Stirling-PDF/releases"
|
||||
<a th:if="${@shouldShow}" href="https://github.com/Stirling-Tools/Stirling-PDF/releases"
|
||||
class="btn btn-sm btn-outline-primary mx-1" id="update-btn" th:utext="#{settings.update}" role="button"
|
||||
target="_blank"></a>
|
||||
</div>
|
||||
@@ -389,14 +390,19 @@
|
||||
<span id="zipThresholdValue" class="ms-2"></span>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox" id="boredWaiting">
|
||||
<input type="checkbox" id="boredWaiting" th:title="#{settings.bored.help}">
|
||||
<label for="boredWaiting" th:text="#{bored}"></label>
|
||||
</div>
|
||||
<a th:if="${@loginEnabled}" href="account" class="btn btn-sm btn-outline-primary" role="button"
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox" id="cacheInputs" th:title="#{settings.cacheInputs.help}">
|
||||
<label for="cacheInputs" th:text="#{settings.cacheInputs.name}"></label>
|
||||
</div>
|
||||
|
||||
<a th:if="${@loginEnabled and @activSecurity}" href="account" class="btn btn-sm btn-outline-primary" role="button"
|
||||
th:text="#{settings.accountSettings}" target="_blank">Account Settings</a>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a th:if="${@loginEnabled}" class="btn btn-danger" role="button" th:text="#{settings.signOut}"
|
||||
<a th:if="${@loginEnabled and @activSecurity}" class="btn btn-danger" role="button" th:text="#{settings.signOut}"
|
||||
href="logout">Sign Out</a>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}"></button>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
<br class="d-md-none">
|
||||
<!-- Features -->
|
||||
<script src="js/homecard.js"></script>
|
||||
|
||||
<div class=" container">
|
||||
<br>
|
||||
<span class="material-symbols-rounded search-icon">
|
||||
@@ -30,6 +29,22 @@
|
||||
</span>
|
||||
<input type="text" id="searchBar" onkeyup="filterCards()" th:placeholder="#{home.searchBar}" autofocus>
|
||||
<div class="features-container">
|
||||
|
||||
<div th:if="${@shouldShow}" class="feature-card favorite" id="update-link" style="display: none;">
|
||||
<a href="https://github.com/Stirling-Tools/Stirling-PDF/releases" target="_blank" class="nav-link text-body-secondary px-2" rel="noopener">
|
||||
<div class="d-flex align-items-center">
|
||||
<!-- Replace the existing icon markup with the new one -->
|
||||
<div class="icon" alt="icon" th:class="advance">
|
||||
<img class="card-icon home-card-icon home-card-icon-colour" src="images/update.svg" alt="Icon" width="30" height="30">
|
||||
</div>
|
||||
<!-- End of icon markup replacement -->
|
||||
<h5 class="card-title lookatme ms-2" th:text="#{settings.update}" th:data-lookatme-text="#{settings.update}"></h5>
|
||||
</div>
|
||||
<p class="card-text" id="app-update"></p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', toolIcon='family_history', tags=#{pipeline.tags}, toolGroup='advance')}">
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
const defaultLocale = document.documentElement.getAttribute('language') || 'en_GB';
|
||||
const defaultLocale = document.documentElement.getAttribute('data-language') || 'en_GB';
|
||||
const storedLocale = localStorage.getItem('languageCode') || defaultLocale;
|
||||
|
||||
const currentURL = new URL(window.location.href);
|
||||
@@ -120,9 +120,17 @@
|
||||
<div class="text-center">
|
||||
<img class="mb-4" src="favicon.svg?v=2" alt="" width="144" height="144">
|
||||
<h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
|
||||
<div th:if="${oAuth2Enabled}">
|
||||
<a href="oauth2/authorization/oidc" class="w-100 btn btn-lg btn-primary" th:text="#{login.ssoSignIn}">Login Via SSO</a>
|
||||
<div class="text-danger text-center">
|
||||
<div th:if="${error == 'oauth2AutoCreateDisabled'}" th:text="#{login.oauth2AutoCreateDisabled}">OAUTH2 Auto-Create User Disabled.</div>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
<h2 class="h5 mb-3 fw-normal" th:text="#{login.signinTitle}">Please sign in</h2>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" id="username" name="username" placeholder="admin">
|
||||
<label for="username" th:text="#{username}">Username</label>
|
||||
|
||||
29
src/main/resources/templates/misc/fake-scan.html
Normal file
29
src/main/resources/templates/misc/fake-scan.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{fakeScan.title}, header=#{fakeScan.header})}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 th:text="#{fakeScan.header}"></h2>
|
||||
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/misc/fake-scan}">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{fakeScan.submit}"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
src/main/resources/templates/misc/print-file.html
Normal file
35
src/main/resources/templates/misc/print-file.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{printFile.title}, header=#{printFile.header})}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 th:text="#{printFile.header}"></h2>
|
||||
<form action="#" th:action="@{api/v1/misc/print-file}" method="post" enctype="multipart/form-data">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf,image/*')}"></div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h4 th:text="#{printFile.selectText.1}">Select Printer</h4> <!-- Assuming the message code printFile.selectText.3 corresponds to "Select Printer" -->
|
||||
<label for="printerName" th:text="#{printFile.selectText.2}">Printer Name:</label> <!-- Assuming the message code printFile.selectText.4 corresponds to "Printer Name:" -->
|
||||
<input type="text" name="printerName" id="printerName" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{printFile.submit}"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user