mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-06 18:30:57 +00:00
commit
620b954336
2
.gitignore
vendored
2
.gitignore
vendored
@ -116,7 +116,7 @@ watchedFolders/
|
|||||||
*.zip
|
*.zip
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
|
*.db
|
||||||
/build
|
/build
|
||||||
|
|
||||||
/.vscode
|
/.vscode
|
29
README.md
29
README.md
@ -185,21 +185,30 @@ For those wanting to use Stirling-PDFs backend API to link with their own custom
|
|||||||
[here](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation (Or by following the API button in your settings of Stirling-PDF)
|
[here](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation (Or by following the API button in your settings of Stirling-PDF)
|
||||||
|
|
||||||
|
|
||||||
|
## Login authentication (CURRENTLY ALPHA TAG ONLY)
|
||||||
|
### Prerequisites:
|
||||||
|
- User must have the folder ./configs volumed within docker so that it is retained during updates.
|
||||||
|
- The environment variable 'login.enabled' must be set to true
|
||||||
|
- The environment variables "INITIAL_USERNAME" and "INITIAL_PASSWORD" must also be populated (only required on first boot to create initial user, ignored after.)
|
||||||
|
|
||||||
|
Once the above has been done, on restart a new stirling-pdf-DB.mv.db will show if everything worked.
|
||||||
|
|
||||||
|
When you login to Stirling PDF you will be redirected to /login page to login with those credentials. After login everything should function as normal
|
||||||
|
|
||||||
|
To access your account settings go to Account settings in the settings cog menu (top right in navbar) this Account settings menu is also where you find your API key.
|
||||||
|
|
||||||
|
To add new users go to bottom of Account settings and hit 'Admin Settings', here you can add new users. The different roles mentioned within this are for rate limiting. This is a Work in progress which will be expanding on more in future
|
||||||
|
|
||||||
|
For API usage you must provide a header with 'X-API-Key' and the associated API key for that user.
|
||||||
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### Q1: Can you add authentication in Stirling PDF?
|
### Q1: What are your planned features?
|
||||||
There is no Auth within Stirling PDF and there is none planned. This feature will not be added. Instead we recommended you use trusted and secure authentication software like Authentik or Authelia.
|
|
||||||
|
|
||||||
### Q2: What are your planned features?
|
|
||||||
- Crop
|
|
||||||
- Progress bar/Tracking
|
- Progress bar/Tracking
|
||||||
- Full custom logic pipelines to combine multiple operations together.
|
- Full custom logic pipelines to combine multiple operations together.
|
||||||
- Folder support with auto scanning to perform operations on
|
- Folder support with auto scanning to perform operations on
|
||||||
- Redact sections of pages
|
- Redact sections of pages
|
||||||
- Add page numbers
|
|
||||||
- Auto rename (Renames file based on file title text)
|
|
||||||
- URL to PDF
|
|
||||||
- Change contrast
|
|
||||||
|
|
||||||
### Q3: Why is my application downloading .htm files?
|
### Q2: Why is my application downloading .htm files?
|
||||||
This is a issue caused commonly by your NGINX congifuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. client_max_body_size SIZE; Where "SIZE" is 50M for example for 50MB files.
|
This is a issue caused commonly by your NGINX congifuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. client_max_body_size SIZE; Where "SIZE" is 50M for example for 50MB files.
|
||||||
|
@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = 'stirling.software'
|
group = 'stirling.software'
|
||||||
version = '0.12.3'
|
version = '0.13.0'
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@ -48,7 +48,12 @@ dependencies {
|
|||||||
implementation 'org.yaml:snakeyaml:2.1'
|
implementation 'org.yaml:snakeyaml:2.1'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.2'
|
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.2'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.1.2'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.1.2'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-security:3.1.2'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.2'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.2'
|
||||||
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
|
||||||
|
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
|
||||||
|
implementation "com.h2database:h2"
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
|
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
|
||||||
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
|
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
|
||||||
implementation 'commons-io:commons-io:2.13.0'
|
implementation 'commons-io:commons-io:2.13.0'
|
||||||
@ -65,6 +70,8 @@ dependencies {
|
|||||||
implementation group: 'com.google.zxing', name: 'core', version: '3.5.1'
|
implementation group: 'com.google.zxing', name: 'core', version: '3.5.1'
|
||||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||||
implementation 'org.commonmark:commonmark:0.21.0'
|
implementation 'org.commonmark:commonmark:0.21.0'
|
||||||
|
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
|
||||||
|
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
|
||||||
|
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
|
|
||||||
|
@ -10,11 +10,14 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableWebSecurity()
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
//@EnableScheduling
|
//@EnableScheduling
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
|
||||||
|
@ -5,6 +5,27 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class AppConfig {
|
public class AppConfig {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Bean(name = "rateLimit")
|
||||||
|
public boolean rateLimit() {
|
||||||
|
String appName = System.getProperty("rateLimit");
|
||||||
|
if (appName == null)
|
||||||
|
appName = System.getenv("rateLimit");
|
||||||
|
System.out.println("rateLimit=" + appName);
|
||||||
|
return (appName != null) ? Boolean.valueOf(appName) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "loginEnabled")
|
||||||
|
public boolean loginEnabled() {
|
||||||
|
String appName = System.getProperty("login.enabled");
|
||||||
|
if (appName == null)
|
||||||
|
appName = System.getenv("login.enabled");
|
||||||
|
System.out.println("loginEnabled=" + appName);
|
||||||
|
return (appName != null) ? Boolean.valueOf(appName) : false;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = "appName")
|
@Bean(name = "appName")
|
||||||
public String appName() {
|
public String appName() {
|
||||||
String appName = System.getProperty("APP_HOME_NAME");
|
String appName = System.getProperty("APP_HOME_NAME");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -10,6 +11,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||||
|
|
||||||
|
import io.github.bucket4j.Bandwidth;
|
||||||
|
import io.github.bucket4j.Bucket;
|
||||||
|
import io.github.bucket4j.Bucket4j;
|
||||||
|
import io.github.bucket4j.Refill;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class Beans implements WebMvcConfigurer {
|
public class Beans implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
|
|
||||||
public class CleanUrlInterceptor implements HandlerInterceptor {
|
public class CleanUrlInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
private static final List<String> ALLOWED_PARAMS = Arrays.asList("lang", "endpoint", "endpoints");
|
private static final List<String> ALLOWED_PARAMS = Arrays.asList("lang", "endpoint", "endpoints", "logout", "error");
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||||
@ -39,9 +40,12 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
|
|||||||
String[] queryParameters = queryString.split("&");
|
String[] queryParameters = queryString.split("&");
|
||||||
for (String param : queryParameters) {
|
for (String param : queryParameters) {
|
||||||
String[] keyValue = param.split("=");
|
String[] keyValue = param.split("=");
|
||||||
|
System.out.print("astirli " + keyValue[0]);
|
||||||
if (keyValue.length != 2) {
|
if (keyValue.length != 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
System.out.print("astirli2 " + keyValue[0]);
|
||||||
|
|
||||||
if (ALLOWED_PARAMS.contains(keyValue[0])) {
|
if (ALLOWED_PARAMS.contains(keyValue[0])) {
|
||||||
parameters.put(keyValue[0], keyValue[1]);
|
parameters.put(keyValue[0], keyValue[1]);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import io.github.bucket4j.Bandwidth;
|
||||||
|
import io.github.bucket4j.Bucket;
|
||||||
|
import io.github.bucket4j.Bucket4j;
|
||||||
|
import io.github.bucket4j.ConsumptionProbe;
|
||||||
|
import io.github.bucket4j.Refill;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import stirling.software.SPDF.model.Role;
|
||||||
|
@Component
|
||||||
|
public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final Map<String, Bucket> apiBuckets = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, Bucket> webBuckets = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("rateLimit")
|
||||||
|
public boolean rateLimit;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
if (!rateLimit) {
|
||||||
|
// If rateLimit is not enabled, just pass all requests without rate limiting
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String method = request.getMethod();
|
||||||
|
if (!"POST".equalsIgnoreCase(method)) {
|
||||||
|
// If the request is not a POST, just pass it through without rate limiting
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String identifier = null;
|
||||||
|
|
||||||
|
// Check for API key in the request headers
|
||||||
|
String apiKey = request.getHeader("X-API-Key");
|
||||||
|
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
||||||
|
identifier = "API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames
|
||||||
|
} else {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
|
identifier = userDetails.getUsername();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If neither API key nor an authenticated user is present, use IP address
|
||||||
|
if (identifier == null) {
|
||||||
|
identifier = request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
Role userRole = getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
|
||||||
|
if (request.getHeader("X-API-Key") != null) {
|
||||||
|
// It's an API call
|
||||||
|
processRequest(userRole.getApiCallsPerDay(), identifier, apiBuckets, request, response, filterChain);
|
||||||
|
} else {
|
||||||
|
// It's a Web UI call
|
||||||
|
processRequest(userRole.getWebCallsPerDay(), identifier, webBuckets, request, response, filterChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Role getRoleFromAuthentication(Authentication authentication) {
|
||||||
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
|
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
||||||
|
try {
|
||||||
|
return Role.fromString(authority.getAuthority());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
// Ignore and continue to next authority.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("User does not have a valid role.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRequest(int limitPerDay, String identifier, Map<String, Bucket> buckets,
|
||||||
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
Bucket userBucket = buckets.computeIfAbsent(identifier, k -> createUserBucket(limitPerDay));
|
||||||
|
ConsumptionProbe probe = userBucket.tryConsumeAndReturnRemaining(1);
|
||||||
|
|
||||||
|
if (probe.isConsumed()) {
|
||||||
|
response.setHeader("X-Rate-Limit-Remaining", Long.toString(probe.getRemainingTokens()));
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
} else {
|
||||||
|
long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000;
|
||||||
|
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
|
||||||
|
response.setHeader("X-Rate-Limit-Retry-After-Seconds", String.valueOf(waitForRefill));
|
||||||
|
response.getWriter().write("Rate limit exceeded for POST requests.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bucket createUserBucket(int limitPerDay) {
|
||||||
|
Bandwidth limit = Bandwidth.classic(limitPerDay, Refill.intervally(limitPerDay, Duration.ofDays(1)));
|
||||||
|
return Bucket.builder().addLimit(limit).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
|||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
// Handler for external static resources
|
// Handler for external static resources
|
||||||
registry.addResourceHandler("/**")
|
registry.addResourceHandler("/**")
|
||||||
.addResourceLocations("file:customFiles/static/", "classpath:/static/")
|
.addResourceLocations("file:customFiles/static/", "classpath:/static/");
|
||||||
.setCachePeriod(0); // Optional: disable caching
|
//.setCachePeriod(0); // Optional: disable caching
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.LockedException;
|
||||||
|
|
||||||
|
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
||||||
|
setDefaultFailureUrl("/login?error=badcredentials");
|
||||||
|
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||||
|
setDefaultFailureUrl("/login?error=locked");
|
||||||
|
}
|
||||||
|
super.onAuthenticationFailure(request, response, exception);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.Authority;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
User user = userRepository.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new UsernameNotFoundException("No user found with username: " + username));
|
||||||
|
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
user.getUsername(),
|
||||||
|
user.getPassword(),
|
||||||
|
user.isEnabled(),
|
||||||
|
true, true, true,
|
||||||
|
getAuthorities(user.getAuthorities())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<? extends GrantedAuthority> getAuthorities(Set<Authority> authorities) {
|
||||||
|
return authorities.stream()
|
||||||
|
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import stirling.software.SPDF.model.Role;
|
||||||
|
@Component
|
||||||
|
public class InitialSetup {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
if(!userService.hasUsers()) {
|
||||||
|
String initialUsername = System.getenv("INITIAL_USERNAME");
|
||||||
|
String initialPassword = System.getenv("INITIAL_PASSWORD");
|
||||||
|
if(initialUsername != null && initialPassword != null) {
|
||||||
|
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||||
|
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.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
@Configuration
|
||||||
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
@Autowired
|
||||||
|
@Lazy
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("loginEnabled")
|
||||||
|
public boolean loginEnabledValue;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserAuthenticationFilter userAuthenticationFilter;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
|
if(loginEnabledValue) {
|
||||||
|
|
||||||
|
http.csrf().disable();
|
||||||
|
http
|
||||||
|
.formLogin(formLogin -> formLogin
|
||||||
|
.loginPage("/login")
|
||||||
|
.defaultSuccessUrl("/")
|
||||||
|
.failureHandler(new CustomAuthenticationFailureHandler())
|
||||||
|
.permitAll()
|
||||||
|
)
|
||||||
|
.logout(logout -> logout
|
||||||
|
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
||||||
|
.logoutSuccessUrl("/login?logout=true")
|
||||||
|
.invalidateHttpSession(true) // Invalidate session
|
||||||
|
.deleteCookies("JSESSIONID")
|
||||||
|
)
|
||||||
|
.authorizeHttpRequests(authz -> authz
|
||||||
|
.requestMatchers(req -> req.getRequestURI().startsWith("/login") || req.getRequestURI().endsWith(".svg") || req.getRequestURI().startsWith("/register") || req.getRequestURI().startsWith("/error") || req.getRequestURI().startsWith("/images/") || req.getRequestURI().startsWith("/public/") || req.getRequestURI().startsWith("/css/") || req.getRequestURI().startsWith("/js/"))
|
||||||
|
.permitAll()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.userDetailsService(userDetailsService)
|
||||||
|
.authenticationProvider(authenticationProvider());
|
||||||
|
} else {
|
||||||
|
http
|
||||||
|
.csrf().disable()
|
||||||
|
.authorizeHttpRequests(authz -> authz
|
||||||
|
.anyRequest().permitAll()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DaoAuthenticationProvider authenticationProvider() {
|
||||||
|
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||||
|
authProvider.setUserDetailsService(userDetailsService);
|
||||||
|
authProvider.setPasswordEncoder(passwordEncoder());
|
||||||
|
return authProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,120 @@
|
|||||||
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import io.github.bucket4j.Bandwidth;
|
||||||
|
import io.github.bucket4j.Bucket;
|
||||||
|
import io.github.bucket4j.Bucket4j;
|
||||||
|
import io.github.bucket4j.ConsumptionProbe;
|
||||||
|
import io.github.bucket4j.Refill;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
||||||
|
@Component
|
||||||
|
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Lazy
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("loginEnabled")
|
||||||
|
public boolean loginEnabledValue;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
|
||||||
|
if (!loginEnabledValue) {
|
||||||
|
// If login is not enabled, just pass all requests without authentication
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
|
// Check for API key in the request headers if no authentication exists
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
String apiKey = request.getHeader("X-API-Key");
|
||||||
|
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
// Use API key to authenticate. This requires you to have an authentication provider for API keys.
|
||||||
|
UserDetails userDetails = userService.loadUserByApiKey(apiKey);
|
||||||
|
if(userDetails == null)
|
||||||
|
{
|
||||||
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
|
response.getWriter().write("Invalid API Key.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
authentication = new ApiKeyAuthenticationToken(userDetails, apiKey, userDetails.getAuthorities());
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
// If API key authentication fails, deny the request
|
||||||
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
|
response.getWriter().write("Invalid API Key.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we still don't have any authentication, deny the request
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
String method = request.getMethod();
|
||||||
|
if ("GET".equalsIgnoreCase(method)) {
|
||||||
|
response.sendRedirect("/login"); // redirect to the login page
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
|
response.getWriter().write("Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternativly you can disable authentication if this is unexpected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
|
||||||
|
String[] permitAllPatterns = {
|
||||||
|
"/login",
|
||||||
|
"/register",
|
||||||
|
"/error",
|
||||||
|
"/images/",
|
||||||
|
"/public/",
|
||||||
|
"/css/",
|
||||||
|
"/js/"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String pattern : permitAllPatterns) {
|
||||||
|
if (uri.startsWith(pattern) || uri.endsWith(".svg")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
package stirling.software.SPDF.config.security;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
|
import stirling.software.SPDF.model.Authority;
|
||||||
|
import stirling.software.SPDF.model.Role;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
public Authentication getAuthentication(String apiKey) {
|
||||||
|
User user = getUserByApiKey(apiKey);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UsernameNotFoundException("API key is not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the user into an Authentication object
|
||||||
|
return new UsernamePasswordAuthenticationToken(
|
||||||
|
user, // principal (typically the user)
|
||||||
|
null, // credentials (we don't expose the password or API key here)
|
||||||
|
getAuthorities(user) // user's authorities (roles/permissions)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
|
||||||
|
// Convert each Authority object into a SimpleGrantedAuthority object.
|
||||||
|
return user.getAuthorities().stream()
|
||||||
|
.map((Authority authority) -> new SimpleGrantedAuthority(authority.getAuthority()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateApiKey() {
|
||||||
|
String apiKey;
|
||||||
|
do {
|
||||||
|
apiKey = UUID.randomUUID().toString();
|
||||||
|
} while (userRepository.findByApiKey(apiKey) != null); // Ensure uniqueness
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User addApiKeyToUser(String username) {
|
||||||
|
User user = userRepository.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
|
|
||||||
|
user.setApiKey(generateApiKey());
|
||||||
|
return userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User refreshApiKeyForUser(String username) {
|
||||||
|
return addApiKeyToUser(username); // reuse the add API key method for refreshing
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiKeyForUser(String username) {
|
||||||
|
User user = userRepository.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
|
return user.getApiKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidApiKey(String apiKey) {
|
||||||
|
return userRepository.findByApiKey(apiKey) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUserByApiKey(String apiKey) {
|
||||||
|
return userRepository.findByApiKey(apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDetails loadUserByApiKey(String apiKey) {
|
||||||
|
User userOptional = userRepository.findByApiKey(apiKey);
|
||||||
|
if (userOptional != null) {
|
||||||
|
User user = userOptional;
|
||||||
|
// Convert your User entity to a UserDetails object with authorities
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
user.getUsername(),
|
||||||
|
user.getPassword(), // you might not need this for API key auth
|
||||||
|
getAuthorities(user)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null; // or throw an exception
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean validateApiKeyForUser(String username, String apiKey) {
|
||||||
|
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||||
|
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveUser(String username, String password) {
|
||||||
|
User user = new User();
|
||||||
|
user.setUsername(username);
|
||||||
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
|
user.setEnabled(true);
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveUser(String username, String password, String role) {
|
||||||
|
User user = new User();
|
||||||
|
user.setUsername(username);
|
||||||
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
|
user.addAuthority(new Authority(role, user));
|
||||||
|
user.setEnabled(true);
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteUser(String username) {
|
||||||
|
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||||
|
if (userOpt.isPresent()) {
|
||||||
|
userRepository.delete(userOpt.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean usernameExists(String username) {
|
||||||
|
return userRepository.findByUsername(username).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUsers() {
|
||||||
|
return userRepository.count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUserSettings(String username, Map<String, String> updates) {
|
||||||
|
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||||
|
if (userOpt.isPresent()) {
|
||||||
|
User user = userOpt.get();
|
||||||
|
Map<String, String> settingsMap = user.getSettings();
|
||||||
|
|
||||||
|
if(settingsMap == null) {
|
||||||
|
settingsMap = new HashMap<String,String>();
|
||||||
|
}
|
||||||
|
settingsMap.clear();
|
||||||
|
settingsMap.putAll(updates);
|
||||||
|
user.setSettings(settingsMap);
|
||||||
|
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<User> findByUsername(String username) {
|
||||||
|
return userRepository.findByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeUsername(User user, String newUsername) {
|
||||||
|
user.setUsername(newUsername);
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changePassword(User user, String newPassword) {
|
||||||
|
user.setPassword(passwordEncoder.encode(newPassword));
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPasswordCorrect(User user, String currentPassword) {
|
||||||
|
return passwordEncoder.matches(currentPassword, user.getPassword());
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,13 @@
|
|||||||
package stirling.software.SPDF.controller.api;
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@ -11,10 +17,11 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RequestPart;
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@ -26,55 +33,93 @@ public class MergeController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
||||||
|
|
||||||
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
|
||||||
// Create a new empty document
|
|
||||||
PDDocument mergedDoc = new PDDocument();
|
|
||||||
|
|
||||||
// Iterate over the list of documents and add their pages to the merged document
|
|
||||||
for (PDDocument doc : documents) {
|
|
||||||
// Get all pages from the current document
|
|
||||||
PDPageTree pages = doc.getPages();
|
|
||||||
// Iterate over the pages and add them to the merged document
|
|
||||||
for (PDPage page : pages) {
|
|
||||||
mergedDoc.addPage(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||||
|
PDDocument mergedDoc = new PDDocument();
|
||||||
|
for (PDDocument doc : documents) {
|
||||||
|
for (PDPage page : doc.getPages()) {
|
||||||
|
mergedDoc.addPage(page);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return mergedDoc;
|
||||||
|
}
|
||||||
|
|
||||||
// Return the merged document
|
private Comparator<MultipartFile> getSortComparator(String sortType) {
|
||||||
return mergedDoc;
|
switch (sortType) {
|
||||||
|
case "byFileName":
|
||||||
|
return Comparator.comparing(MultipartFile::getOriginalFilename);
|
||||||
|
case "byDateModified":
|
||||||
|
return (file1, file2) -> {
|
||||||
|
try {
|
||||||
|
BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
|
||||||
|
BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
|
||||||
|
return attr1.lastModifiedTime().compareTo(attr2.lastModifiedTime());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return 0; // If there's an error, treat them as equal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case "byDateCreated":
|
||||||
|
return (file1, file2) -> {
|
||||||
|
try {
|
||||||
|
BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
|
||||||
|
BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
|
||||||
|
return attr1.creationTime().compareTo(attr2.creationTime());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return 0; // If there's an error, treat them as equal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case "byPDFTitle":
|
||||||
|
return (file1, file2) -> {
|
||||||
|
try (PDDocument doc1 = PDDocument.load(file1.getInputStream());
|
||||||
|
PDDocument doc2 = PDDocument.load(file2.getInputStream())) {
|
||||||
|
String title1 = doc1.getDocumentInformation().getTitle();
|
||||||
|
String title2 = doc2.getDocumentInformation().getTitle();
|
||||||
|
return title1.compareTo(title2);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case "orderProvided":
|
||||||
|
default:
|
||||||
|
return (file1, file2) -> 0; // Default is the order provided
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs")
|
||||||
|
@Operation(summary = "Merge multiple PDF files into one",
|
||||||
|
description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO")
|
||||||
|
public ResponseEntity<byte[]> mergePdfs(
|
||||||
|
@RequestPart(required = true, value = "fileInput") MultipartFile[] files,
|
||||||
|
@RequestParam(value = "sortType", defaultValue = "orderProvided")
|
||||||
|
@Parameter(schema = @Schema(description = "The type of sorting to be applied on the input files before merging.",
|
||||||
|
allowableValues = {
|
||||||
|
"orderProvided",
|
||||||
|
"byFileName",
|
||||||
|
"byDateModified",
|
||||||
|
"byDateCreated",
|
||||||
|
"byPDFTitle"
|
||||||
|
}))
|
||||||
|
String sortType) throws IOException {
|
||||||
|
|
||||||
|
Arrays.sort(files, getSortComparator(sortType));
|
||||||
|
|
||||||
|
List<PDDocument> documents = new ArrayList<>();
|
||||||
|
for (MultipartFile file : files) {
|
||||||
|
try (InputStream is = file.getInputStream()) {
|
||||||
|
documents.add(PDDocument.load(is));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs")
|
try (PDDocument mergedDoc = mergeDocuments(documents)) {
|
||||||
@Operation(
|
|
||||||
summary = "Merge multiple PDF files into one",
|
|
||||||
description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO"
|
|
||||||
)
|
|
||||||
public ResponseEntity<byte[]> mergePdfs(
|
|
||||||
@RequestPart(required = true, value = "fileInput")
|
|
||||||
@Parameter(description = "The input PDF files to be merged into a single file", required = true)
|
|
||||||
MultipartFile[] files) throws IOException {
|
|
||||||
// Read the input PDF files into PDDocument objects
|
|
||||||
List<PDDocument> documents = new ArrayList<>();
|
|
||||||
|
|
||||||
// Loop through the files array and read each file into a PDDocument
|
|
||||||
for (MultipartFile file : files) {
|
|
||||||
documents.add(PDDocument.load(file.getInputStream()));
|
|
||||||
}
|
|
||||||
|
|
||||||
PDDocument mergedDoc = mergeDocuments(documents);
|
|
||||||
|
|
||||||
|
|
||||||
// Return the merged PDF as a response
|
|
||||||
ResponseEntity<byte[]> response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
ResponseEntity<byte[]> response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
||||||
|
|
||||||
for (PDDocument doc : documents) {
|
|
||||||
// Close the document after processing
|
|
||||||
doc.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
} finally {
|
||||||
|
for (PDDocument doc : documents) {
|
||||||
|
if (doc != null) {
|
||||||
|
doc.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@PostMapping("/register")
|
||||||
|
public String register(@RequestParam String username, @RequestParam String password, Model model) {
|
||||||
|
if(userService.usernameExists(username)) {
|
||||||
|
model.addAttribute("error", "Username already exists");
|
||||||
|
return "register";
|
||||||
|
}
|
||||||
|
|
||||||
|
userService.saveUser(username, password);
|
||||||
|
return "redirect:/login?registered=true";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/change-username")
|
||||||
|
public ResponseEntity<String> changeUsername(Principal principal, @RequestParam String currentPassword, @RequestParam String newUsername, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
if (principal == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("User not authenticated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
||||||
|
|
||||||
|
if(userOpt == null || userOpt.isEmpty()) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User not found.");
|
||||||
|
}
|
||||||
|
User user = userOpt.get();
|
||||||
|
|
||||||
|
if(!userService.isPasswordCorrect(user, currentPassword)) {
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Current password is incorrect.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(userService.usernameExists(newUsername)) {
|
||||||
|
return ResponseEntity.status(HttpStatus.CONFLICT).body("New username already exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
userService.changeUsername(user, newUsername);
|
||||||
|
|
||||||
|
// Logout using Spring's utility
|
||||||
|
new SecurityContextLogoutHandler().logout(request, response, null);
|
||||||
|
|
||||||
|
|
||||||
|
return ResponseEntity.ok("Username updated successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/change-password")
|
||||||
|
public ResponseEntity<String> changePassword(Principal principal, @RequestParam String currentPassword, @RequestParam String newPassword, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
if (principal == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("User not authenticated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
||||||
|
|
||||||
|
if(userOpt == null || userOpt.isEmpty()) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User not found.");
|
||||||
|
}
|
||||||
|
User user = userOpt.get();
|
||||||
|
if(!userService.isPasswordCorrect(user, currentPassword)) {
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Current password is incorrect.");
|
||||||
|
}
|
||||||
|
|
||||||
|
userService.changePassword(user, passwordEncoder.encode(newPassword));
|
||||||
|
|
||||||
|
// Logout using Spring's utility
|
||||||
|
new SecurityContextLogoutHandler().logout(request, response, null);
|
||||||
|
|
||||||
|
return ResponseEntity.ok("Password updated successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/updateUserSettings")
|
||||||
|
public String updateUserSettings(HttpServletRequest request, Principal principal) {
|
||||||
|
Map<String, String[]> paramMap = request.getParameterMap();
|
||||||
|
Map<String, String> updates = new HashMap<>();
|
||||||
|
|
||||||
|
System.out.println("Received parameter map: " + paramMap);
|
||||||
|
|
||||||
|
for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
|
||||||
|
updates.put(entry.getKey(), entry.getValue()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Processed updates: " + updates);
|
||||||
|
|
||||||
|
// Assuming you have a method in userService to update the settings for a user
|
||||||
|
userService.updateUserSettings(principal.getName(), updates);
|
||||||
|
|
||||||
|
return "redirect:/account"; // Redirect to a page of your choice after updating
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
|
@PostMapping("/admin/saveUser")
|
||||||
|
public String saveUser(@RequestParam String username, @RequestParam String password, @RequestParam String role) {
|
||||||
|
userService.saveUser(username, password, role);
|
||||||
|
return "redirect:/addUsers"; // Redirect to account page after adding the user
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
|
@GetMapping("/admin/deleteUser/{username}")
|
||||||
|
public String deleteUser(@PathVariable String username) {
|
||||||
|
userService.deleteUser(username);
|
||||||
|
return "redirect:/addUsers";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/get-api-key")
|
||||||
|
public ResponseEntity<String> getApiKey(Principal principal) {
|
||||||
|
if (principal == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("User not authenticated.");
|
||||||
|
}
|
||||||
|
String username = principal.getName();
|
||||||
|
String apiKey = userService.getApiKeyForUser(username);
|
||||||
|
if (apiKey == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("API key not found for user.");
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/update-api-key")
|
||||||
|
public ResponseEntity<String> updateApiKey(Principal principal) {
|
||||||
|
if (principal == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("User not authenticated.");
|
||||||
|
}
|
||||||
|
String username = principal.getName();
|
||||||
|
User user = userService.refreshApiKeyForUser(username);
|
||||||
|
String apiKey = user.getApiKey();
|
||||||
|
if (apiKey == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("API key not found for user.");
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -52,13 +52,13 @@ public class PasswordController {
|
|||||||
@RequestPart(required = true, value = "fileInput")
|
@RequestPart(required = true, value = "fileInput")
|
||||||
@Parameter(description = "The input PDF file to which the password should be added", required = true)
|
@Parameter(description = "The input PDF file to which the password should be added", required = true)
|
||||||
MultipartFile fileInput,
|
MultipartFile fileInput,
|
||||||
@RequestParam(value = "", name = "ownerPassword")
|
@RequestParam(value = "", name = "ownerPassword", required = false, defaultValue = "")
|
||||||
@Parameter(description = "The owner password to be added to the PDF file (Restricts what can be done with the document once it is opened)")
|
@Parameter(description = "The owner password to be added to the PDF file (Restricts what can be done with the document once it is opened)")
|
||||||
String ownerPassword,
|
String ownerPassword,
|
||||||
@RequestParam( name = "password", required = false)
|
@RequestParam( name = "password", required = false, defaultValue = "")
|
||||||
@Parameter(description = "The password to be added to the PDF file (Restricts the opening of the document itself.)")
|
@Parameter(description = "The password to be added to the PDF file (Restricts the opening of the document itself.)")
|
||||||
String password,
|
String password,
|
||||||
@RequestParam( name = "keyLength", required = false)
|
@RequestParam( name = "keyLength", required = false, defaultValue = "256")
|
||||||
@Parameter(description = "The length of the encryption key", schema = @Schema(allowableValues = {"40", "128", "256"}))
|
@Parameter(description = "The length of the encryption key", schema = @Schema(allowableValues = {"40", "128", "256"}))
|
||||||
int keyLength,
|
int keyLength,
|
||||||
@RequestParam( name = "canAssembleDocument", required = false)
|
@RequestParam( name = "canAssembleDocument", required = false)
|
||||||
@ -99,14 +99,14 @@ public class PasswordController {
|
|||||||
ap.setCanPrintFaithful(!canPrintFaithful);
|
ap.setCanPrintFaithful(!canPrintFaithful);
|
||||||
StandardProtectionPolicy spp = new StandardProtectionPolicy(ownerPassword, password, ap);
|
StandardProtectionPolicy spp = new StandardProtectionPolicy(ownerPassword, password, ap);
|
||||||
|
|
||||||
|
if(!"".equals(ownerPassword) || !"".equals(password)) {
|
||||||
|
spp.setEncryptionKeyLength(keyLength);
|
||||||
spp.setEncryptionKeyLength(keyLength);
|
}
|
||||||
|
|
||||||
spp.setPermissions(ap);
|
spp.setPermissions(ap);
|
||||||
|
|
||||||
document.protect(spp);
|
document.protect(spp);
|
||||||
|
|
||||||
|
if("".equals(ownerPassword) && "".equals(password))
|
||||||
|
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_permissions.pdf");
|
||||||
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,13 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -16,18 +18,108 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
@Controller
|
@Controller
|
||||||
@Tag(name = "General", description = "General APIs")
|
@Tag(name = "General", description = "General APIs")
|
||||||
public class GeneralWebController {
|
public class GeneralWebController {
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/login")
|
||||||
|
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
||||||
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
|
return "redirect:/";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.getParameter("error") != null) {
|
||||||
|
|
||||||
|
model.addAttribute("error", request.getParameter("error"));
|
||||||
|
}
|
||||||
|
if (request.getParameter("logout") != null) {
|
||||||
|
|
||||||
|
model.addAttribute("logoutMessage", "You have been logged out.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "login";
|
||||||
|
}
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository; // Assuming you have a repository for user operations
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService; // Assuming you have a repository for user operations
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
|
@GetMapping("/addUsers")
|
||||||
|
public String showAddUserForm(Model model) {
|
||||||
|
List<User> allUsers = userRepository.findAll();
|
||||||
|
model.addAttribute("users", allUsers);
|
||||||
|
return "addUsers";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/account")
|
||||||
|
public String account(HttpServletRequest request, Model model, Authentication authentication) {
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
return "redirect:/";
|
||||||
|
}
|
||||||
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
|
||||||
|
if (principal instanceof UserDetails) {
|
||||||
|
// Cast the principal object to UserDetails
|
||||||
|
UserDetails userDetails = (UserDetails) principal;
|
||||||
|
|
||||||
|
// Retrieve username and other attributes
|
||||||
|
String username = userDetails.getUsername();
|
||||||
|
|
||||||
|
// Fetch user details from the database
|
||||||
|
Optional<User> user = userRepository.findByUsername(username); // Assuming findByUsername method exists
|
||||||
|
if (!user.isPresent()) {
|
||||||
|
// Handle error appropriately
|
||||||
|
return "redirect:/error"; // Example redirection in case of error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert settings map to JSON string
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
String settingsJson;
|
||||||
|
try {
|
||||||
|
settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
// Handle JSON conversion error
|
||||||
|
e.printStackTrace();
|
||||||
|
return "redirect:/error"; // Example redirection in case of error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add attributes to the model
|
||||||
|
model.addAttribute("username", username);
|
||||||
|
model.addAttribute("role", user.get().getRolesAsString());
|
||||||
|
model.addAttribute("settings", settingsJson);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "redirect:/";
|
||||||
|
}
|
||||||
|
return "account";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/pipeline")
|
@GetMapping("/pipeline")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String pipelineForm(Model model) {
|
public String pipelineForm(Model model) {
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package stirling.software.SPDF.model;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken {
|
||||||
|
|
||||||
|
private final Object principal;
|
||||||
|
private Object credentials;
|
||||||
|
|
||||||
|
public ApiKeyAuthenticationToken(String apiKey) {
|
||||||
|
super(null);
|
||||||
|
this.principal = null;
|
||||||
|
this.credentials = apiKey;
|
||||||
|
setAuthenticated(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiKeyAuthenticationToken(Object principal, String apiKey, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
super(authorities);
|
||||||
|
this.principal = principal; // principal can be a UserDetails object
|
||||||
|
this.credentials = apiKey;
|
||||||
|
super.setAuthenticated(true); // this authentication is trusted
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
throw new IllegalArgumentException("Cannot set this token to trusted. Use constructor which takes a GrantedAuthority list instead.");
|
||||||
|
}
|
||||||
|
super.setAuthenticated(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eraseCredentials() {
|
||||||
|
super.eraseCredentials();
|
||||||
|
credentials = null;
|
||||||
|
}
|
||||||
|
}
|
64
src/main/java/stirling/software/SPDF/model/Authority.java
Normal file
64
src/main/java/stirling/software/SPDF/model/Authority.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "authorities")
|
||||||
|
public class Authority {
|
||||||
|
|
||||||
|
public Authority() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Authority(String authority, User user) {
|
||||||
|
this.authority = authority;
|
||||||
|
this.user = user;
|
||||||
|
user.getAuthorities().add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "authority")
|
||||||
|
private String authority;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "user_id")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthority() {
|
||||||
|
return authority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthority(String authority) {
|
||||||
|
this.authority = authority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
50
src/main/java/stirling/software/SPDF/model/Role.java
Normal file
50
src/main/java/stirling/software/SPDF/model/Role.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package stirling.software.SPDF.model;
|
||||||
|
public enum Role {
|
||||||
|
|
||||||
|
// Unlimited access
|
||||||
|
ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
||||||
|
|
||||||
|
// Unlimited access
|
||||||
|
USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
||||||
|
|
||||||
|
// 40 API calls Per Day, 40 web calls
|
||||||
|
LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40),
|
||||||
|
|
||||||
|
// 20 API calls Per Day, 20 web calls
|
||||||
|
EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20),
|
||||||
|
|
||||||
|
// 0 API calls per day and 20 web calls
|
||||||
|
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20);
|
||||||
|
|
||||||
|
private final String roleId;
|
||||||
|
private final int apiCallsPerDay;
|
||||||
|
private final int webCallsPerDay;
|
||||||
|
|
||||||
|
Role(String roleId, int apiCallsPerDay, int webCallsPerDay) {
|
||||||
|
this.roleId = roleId;
|
||||||
|
this.apiCallsPerDay = apiCallsPerDay;
|
||||||
|
this.webCallsPerDay = webCallsPerDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoleId() {
|
||||||
|
return roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getApiCallsPerDay() {
|
||||||
|
return apiCallsPerDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWebCallsPerDay() {
|
||||||
|
return webCallsPerDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Role fromString(String roleId) {
|
||||||
|
for (Role role : Role.values()) {
|
||||||
|
if (role.getRoleId().equalsIgnoreCase(roleId)) {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("No Role defined for id: " + roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
124
src/main/java/stirling/software/SPDF/model/User.java
Normal file
124
src/main/java/stirling/software/SPDF/model/User.java
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.CollectionTable;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.ElementCollection;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.MapKeyColumn;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
@Entity
|
||||||
|
@Table(name = "users")
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "user_id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "username", unique = true)
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Column(name = "password")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Column(name = "apiKey")
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
@Column(name = "enabled")
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
|
||||||
|
private Set<Authority> authorities = new HashSet<>();
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@MapKeyColumn(name = "setting_key")
|
||||||
|
@Column(name = "setting_value")
|
||||||
|
@CollectionTable(name = "user_settings", joinColumns = @JoinColumn(name = "user_id"))
|
||||||
|
private Map<String, String> settings = new HashMap<>(); // Key-value pairs of settings.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiKey() {
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApiKey(String apiKey) {
|
||||||
|
this.apiKey = apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getSettings() {
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSettings(Map<String, String> settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Authority> getAuthorities() {
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorities(Set<Authority> authorities) {
|
||||||
|
this.authorities = authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAuthorities(Set<Authority> authorities) {
|
||||||
|
this.authorities.addAll(authorities);
|
||||||
|
}
|
||||||
|
public void addAuthority(Authority authorities) {
|
||||||
|
this.authorities.add(authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRolesAsString() {
|
||||||
|
return this.authorities.stream()
|
||||||
|
.map(Authority::getAuthority)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package stirling.software.SPDF.repository;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.Authority;
|
||||||
|
|
||||||
|
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
|
||||||
|
//Set<Authority> findByUsername(String username);
|
||||||
|
Set<Authority> findByUser_Username(String username);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package stirling.software.SPDF.repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, String> {
|
||||||
|
Optional<User> findByUsername(String username);
|
||||||
|
User findByApiKey(String apiKey);
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,10 @@ server.error.include-exception=true
|
|||||||
server.error.include-message=always
|
server.error.include-message=always
|
||||||
|
|
||||||
#logging.level.org.springframework.web=DEBUG
|
#logging.level.org.springframework.web=DEBUG
|
||||||
|
#logging.level.org.springframework=DEBUG
|
||||||
|
#logging.level.org.springframework.security=DEBUG
|
||||||
|
|
||||||
|
#login.enabled=true
|
||||||
|
|
||||||
server.servlet.session.tracking-modes=cookie
|
server.servlet.session.tracking-modes=cookie
|
||||||
server.servlet.context-path=${APP_ROOT_PATH:/}
|
server.servlet.context-path=${APP_ROOT_PATH:/}
|
||||||
@ -33,3 +36,11 @@ spring.mvc.async.request-timeout=${ASYNC_CONNECTION_TIMEOUT:300000}
|
|||||||
spring.resources.static-locations=file:customFiles/static/
|
spring.resources.static-locations=file:customFiles/static/
|
||||||
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
||||||
#spring.thymeleaf.cache=false
|
#spring.thymeleaf.cache=false
|
||||||
|
|
||||||
|
|
||||||
|
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||||
|
spring.datasource.driver-class-name=org.h2.Driver
|
||||||
|
spring.datasource.username=sa
|
||||||
|
spring.datasource.password=
|
||||||
|
spring.h2.console.enabled=true
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=\u0641\u062A\u062D \u0641\u064A \u0646\u0641\u0633 \u0
|
|||||||
settings.downloadOption.2=\u0641\u062A\u062D \u0641\u064A \u0646\u0627\u0641\u0630\u0629 \u062C\u062F\u064A\u062F\u0629
|
settings.downloadOption.2=\u0641\u062A\u062D \u0641\u064A \u0646\u0627\u0641\u0630\u0629 \u062C\u062F\u064A\u062F\u0629
|
||||||
settings.downloadOption.3=\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u0644\u0641
|
settings.downloadOption.3=\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u0644\u0641
|
||||||
settings.zipThreshold=\u0645\u0644\u0641\u0627\u062A \u0645\u0636\u063A\u0648\u0637\u0629 \u0639\u0646\u062F \u062A\u062C\u0627\u0648\u0632 \u0639\u062F\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0646\u0632\u064A\u0644\u0647\u0627
|
settings.zipThreshold=\u0645\u0644\u0641\u0627\u062A \u0645\u0636\u063A\u0648\u0637\u0629 \u0639\u0646\u062F \u062A\u062C\u0627\u0648\u0632 \u0639\u062F\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0646\u0632\u064A\u0644\u0647\u0627
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=إضافة صورة
|
|||||||
#merge
|
#merge
|
||||||
merge.title=دمج
|
merge.title=دمج
|
||||||
merge.header=دمج ملفات PDF متعددة (2+)
|
merge.header=دمج ملفات PDF متعددة (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=دمج
|
merge.submit=دمج
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=دوران (0-360):
|
|||||||
watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا):
|
watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا):
|
||||||
watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا):
|
watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا):
|
||||||
watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100\u066A):
|
watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100\u066A):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=إضافة علامة مائية
|
watermark.submit=إضافة علامة مائية
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
|
||||||
remove-watermark.header=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
|
||||||
remove-watermark.selectText.1=\u062D\u062F\u062F PDF \u0644\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646:
|
|
||||||
remove-watermark.selectText.2=\u0646\u0635 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629:
|
|
||||||
remove-watermark.submit=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=تغيير الأذونات
|
permissions.title=تغيير الأذونات
|
||||||
permissions.header=تغيير الأذونات
|
permissions.header=تغيير الأذونات
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Obre mateixa finestra
|
|||||||
settings.downloadOption.2=Obre mateixa finestra
|
settings.downloadOption.2=Obre mateixa finestra
|
||||||
settings.downloadOption.3=Descarrega Arxiu
|
settings.downloadOption.3=Descarrega Arxiu
|
||||||
settings.zipThreshold=Comprimiu els fitxers quan el nombre de fitxers baixats superi
|
settings.zipThreshold=Comprimiu els fitxers quan el nombre de fitxers baixats superi
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Afegir Imatge
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Fusiona
|
merge.title=Fusiona
|
||||||
merge.header=Fusiona múltiples PDFs (2+)
|
merge.header=Fusiona múltiples PDFs (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Fusiona
|
merge.submit=Fusiona
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Rotació (0-360):
|
|||||||
watermark.selectText.5=separació d'amplada (Espai horitzontal entre cada Marca d'Aigua):
|
watermark.selectText.5=separació d'amplada (Espai horitzontal entre cada Marca d'Aigua):
|
||||||
watermark.selectText.6=separació d'alçada (Espai vertical entre cada Marca d'Aigua):
|
watermark.selectText.6=separació d'alçada (Espai vertical entre cada Marca d'Aigua):
|
||||||
watermark.selectText.7=Opacitat (0% - 100%):
|
watermark.selectText.7=Opacitat (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Afegir Marca d'Aigua
|
watermark.submit=Afegir Marca d'Aigua
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Elimina Marca d'Aigua
|
|
||||||
remove-watermark.header=Elimina Marca d'Aigua
|
|
||||||
remove-watermark.selectText.1=Seleciona PDF per eliminar Marca d'Aigua:
|
|
||||||
remove-watermark.selectText.2=Text de la Marca d'Aigua:
|
|
||||||
remove-watermark.submit=Elimina Marca d'Aigua
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Canviar Permissos
|
permissions.title=Canviar Permissos
|
||||||
permissions.header=Canviar Permissos
|
permissions.header=Canviar Permissos
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Im selben Fenster öffnen
|
|||||||
settings.downloadOption.2=In neuem Fenster öffnen
|
settings.downloadOption.2=In neuem Fenster öffnen
|
||||||
settings.downloadOption.3=Datei herunterladen
|
settings.downloadOption.3=Datei herunterladen
|
||||||
settings.zipThreshold=Dateien komprimieren, wenn die Anzahl der heruntergeladenen Dateien überschritten wird
|
settings.zipThreshold=Dateien komprimieren, wenn die Anzahl der heruntergeladenen Dateien überschritten wird
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Bild hinzufügen
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Zusammenführen
|
merge.title=Zusammenführen
|
||||||
merge.header=Mehrere PDFs zusammenführen (2+)
|
merge.header=Mehrere PDFs zusammenführen (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Zusammenführen
|
merge.submit=Zusammenführen
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Drehung (0-360):
|
|||||||
watermark.selectText.5=breiteSpacer (horizontaler Abstand zwischen den einzelnen Wasserzeichen):
|
watermark.selectText.5=breiteSpacer (horizontaler Abstand zwischen den einzelnen Wasserzeichen):
|
||||||
watermark.selectText.6=höheSpacer (vertikaler Abstand zwischen den einzelnen Wasserzeichen):
|
watermark.selectText.6=höheSpacer (vertikaler Abstand zwischen den einzelnen Wasserzeichen):
|
||||||
watermark.selectText.7=Deckkraft (0% - 100 %):
|
watermark.selectText.7=Deckkraft (0% - 100 %):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Wasserzeichen hinzufügen
|
watermark.submit=Wasserzeichen hinzufügen
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Wasserzeichen entfernen
|
|
||||||
remove-watermark.header=Wasserzeichen entfernen
|
|
||||||
remove-watermark.selectText.1=PDF auswählen, um Wasserzeichen zu entfernen von:
|
|
||||||
remove-watermark.selectText.2=Wasserzeichentext:
|
|
||||||
remove-watermark.submit=Wasserzeichen entfernen
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Berechtigungen ändern
|
permissions.title=Berechtigungen ändern
|
||||||
permissions.header=Berechtigungen ändern
|
permissions.header=Berechtigungen ändern
|
||||||
|
@ -31,7 +31,11 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +58,41 @@ settings.downloadOption.1=Open in same window
|
|||||||
settings.downloadOption.2=Open in new window
|
settings.downloadOption.2=Open in new window
|
||||||
settings.downloadOption.3=Download file
|
settings.downloadOption.3=Download file
|
||||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -520,6 +559,8 @@ addImage.submit=Add image
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Merge
|
merge.title=Merge
|
||||||
merge.header=Merge multiple PDFs (2+)
|
merge.header=Merge multiple PDFs (2+)
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Merge
|
merge.submit=Merge
|
||||||
|
|
||||||
|
|
||||||
@ -578,9 +619,9 @@ imageToPDF.selectText.5=Convert to separate PDFs
|
|||||||
pdfToImage.title=PDF to Image
|
pdfToImage.title=PDF to Image
|
||||||
pdfToImage.header=PDF to Image
|
pdfToImage.header=PDF to Image
|
||||||
pdfToImage.selectText=Image Format
|
pdfToImage.selectText=Image Format
|
||||||
pdfToImage.singleOrMultiple=Image result type
|
pdfToImage.singleOrMultiple=Page to Image result type
|
||||||
pdfToImage.single=Single Big Image
|
pdfToImage.single=Single Big Image Combing all pages
|
||||||
pdfToImage.multi=Multiple Images
|
pdfToImage.multi=Multiple Images, one image per page
|
||||||
pdfToImage.colorType=Colour type
|
pdfToImage.colorType=Colour type
|
||||||
pdfToImage.color=Colour
|
pdfToImage.color=Colour
|
||||||
pdfToImage.grey=Greyscale
|
pdfToImage.grey=Greyscale
|
||||||
@ -620,17 +661,11 @@ watermark.selectText.4=Rotation (0-360):
|
|||||||
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
|
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
|
||||||
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
||||||
watermark.selectText.7=Opacity (0% - 100%):
|
watermark.selectText.7=Opacity (0% - 100%):
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Add Watermark
|
watermark.submit=Add Watermark
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Remove Watermark
|
|
||||||
remove-watermark.header=Remove Watermark
|
|
||||||
remove-watermark.selectText.1=Select PDF to remove watermark from:
|
|
||||||
remove-watermark.selectText.2=Watermark Text:
|
|
||||||
remove-watermark.submit=Remove Watermark
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Change Permissions
|
permissions.title=Change Permissions
|
||||||
permissions.header=Change Permissions
|
permissions.header=Change Permissions
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||||
language.direction=ltr
|
language.direction=ltr
|
||||||
|
|
||||||
pdfPrompt=Select PDF(s)
|
pdfPrompt=Select PDF(s)
|
||||||
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Open in same window
|
|||||||
settings.downloadOption.2=Open in new window
|
settings.downloadOption.2=Open in new window
|
||||||
settings.downloadOption.3=Download file
|
settings.downloadOption.3=Download file
|
||||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -520,6 +571,11 @@ addImage.submit=Add image
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Merge
|
merge.title=Merge
|
||||||
merge.header=Merge multiple PDFs (2+)
|
merge.header=Merge multiple PDFs (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Merge
|
merge.submit=Merge
|
||||||
|
|
||||||
|
|
||||||
@ -620,17 +676,14 @@ watermark.selectText.4=Rotation (0-360):
|
|||||||
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
|
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
|
||||||
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
||||||
watermark.selectText.7=Opacity (0% - 100%):
|
watermark.selectText.7=Opacity (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Add Watermark
|
watermark.submit=Add Watermark
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Remove Watermark
|
|
||||||
remove-watermark.header=Remove Watermark
|
|
||||||
remove-watermark.selectText.1=Select PDF to remove watermark from:
|
|
||||||
remove-watermark.selectText.2=Watermark Text:
|
|
||||||
remove-watermark.submit=Remove Watermark
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Change Permissions
|
permissions.title=Change Permissions
|
||||||
permissions.header=Change Permissions
|
permissions.header=Change Permissions
|
||||||
@ -657,7 +710,7 @@ removePassword.submit=Remove
|
|||||||
|
|
||||||
|
|
||||||
#changeMetadata
|
#changeMetadata
|
||||||
changeMetadata.title=Change Metadata
|
changeMetadata.title=Title:
|
||||||
changeMetadata.header=Change Metadata
|
changeMetadata.header=Change Metadata
|
||||||
changeMetadata.selectText.1=Please edit the variables you wish to change
|
changeMetadata.selectText.1=Please edit the variables you wish to change
|
||||||
changeMetadata.selectText.2=Delete all metadata
|
changeMetadata.selectText.2=Delete all metadata
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Mediano
|
|||||||
sizes.large=Grande
|
sizes.large=Grande
|
||||||
sizes.x-large=Extra grande
|
sizes.x-large=Extra grande
|
||||||
error.pdfPassword=El documento PDF está protegido con contraseña y no se ha proporcionado o es incorrecta
|
error.pdfPassword=El documento PDF está protegido con contraseña y no se ha proporcionado o es incorrecta
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Abrir en la misma ventana
|
|||||||
settings.downloadOption.2=Abrir en una nueva ventana
|
settings.downloadOption.2=Abrir en una nueva ventana
|
||||||
settings.downloadOption.3=Descargar el fichero
|
settings.downloadOption.3=Descargar el fichero
|
||||||
settings.zipThreshold=Ficheros ZIP cuando excede el número de ficheros descargados
|
settings.zipThreshold=Ficheros ZIP cuando excede el número de ficheros descargados
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Añadir imagen
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Unir
|
merge.title=Unir
|
||||||
merge.header=Unir múltiples PDFs (2+)
|
merge.header=Unir múltiples PDFs (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Unir
|
merge.submit=Unir
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Rotación (0-360):
|
|||||||
watermark.selectText.5=Ancho (Espacio entre cada marca de agua horizontalmente):
|
watermark.selectText.5=Ancho (Espacio entre cada marca de agua horizontalmente):
|
||||||
watermark.selectText.6=Alto (Espacio entre cada marca de agua verticalmente):
|
watermark.selectText.6=Alto (Espacio entre cada marca de agua verticalmente):
|
||||||
watermark.selectText.7=Opacidad (0% - 100%):
|
watermark.selectText.7=Opacidad (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Añadir marca de agua
|
watermark.submit=Añadir marca de agua
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Eliminar marca de agua
|
|
||||||
remove-watermark.header=Eliminar marca de agua
|
|
||||||
remove-watermark.selectText.1=Seleccionar PDF para eliminar la marca de agua:
|
|
||||||
remove-watermark.selectText.2=Texto de la marca de agua:
|
|
||||||
remove-watermark.submit=Eliminar marca de agua
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Cambiar permisos
|
permissions.title=Cambiar permisos
|
||||||
permissions.header=Cambiar permisos
|
permissions.header=Cambiar permisos
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=PDF dokumentua pasahitzarekin babestuta dago eta pasahitza ez da sartu edo akastuna da
|
error.pdfPassword=PDF dokumentua pasahitzarekin babestuta dago eta pasahitza ez da sartu edo akastuna da
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Ireki leiho berean
|
|||||||
settings.downloadOption.2=Ireki leiho berrian
|
settings.downloadOption.2=Ireki leiho berrian
|
||||||
settings.downloadOption.3=Deskargatu fitxategia
|
settings.downloadOption.3=Deskargatu fitxategia
|
||||||
settings.zipThreshold=ZIP fitxategiak deskargatutako fitxategi kopurua gainditzen denean
|
settings.zipThreshold=ZIP fitxategiak deskargatutako fitxategi kopurua gainditzen denean
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Gehitu irudia
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Elkartu
|
merge.title=Elkartu
|
||||||
merge.header=Elkartu zenbait PDF (2+)
|
merge.header=Elkartu zenbait PDF (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Elkartu
|
merge.submit=Elkartu
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Errotazioa (0-360):
|
|||||||
watermark.selectText.5=Zabalera (ur-marka bakoitzaren arteko espazioa horizontalean):
|
watermark.selectText.5=Zabalera (ur-marka bakoitzaren arteko espazioa horizontalean):
|
||||||
watermark.selectText.6=Altuera (ur-marka bakoitzaren arteko espazioa bertikalean):
|
watermark.selectText.6=Altuera (ur-marka bakoitzaren arteko espazioa bertikalean):
|
||||||
watermark.selectText.7=Opakutasuna (0% - 100%):
|
watermark.selectText.7=Opakutasuna (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Gehitu ur-marka
|
watermark.submit=Gehitu ur-marka
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Ezabatu ur-marka
|
|
||||||
remove-watermark.header=Ezabatu ur-marka
|
|
||||||
remove-watermark.selectText.1=Hautatu PDFa ur-marka ezabatzeko:
|
|
||||||
remove-watermark.selectText.2=Ur-markaren testua:
|
|
||||||
remove-watermark.submit=Ezabatu ur-marka
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Aldatu baimenak
|
permissions.title=Aldatu baimenak
|
||||||
permissions.header=Aldatu baimenak
|
permissions.header=Aldatu baimenak
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||||
language.direction=ltr
|
language.direction=ltr
|
||||||
|
|
||||||
pdfPrompt=Sélectionnez le(s) PDF
|
pdfPrompt=Sélectionnez le(s) PDF
|
||||||
@ -31,7 +31,14 @@ sizes.medium=Moyen
|
|||||||
sizes.large=Grand
|
sizes.large=Grand
|
||||||
sizes.x-large=Très grand
|
sizes.x-large=Très grand
|
||||||
error.pdfPassword=Le document PDF est protégé par un mot de passe et le mot de passe n\u2019a pas été fourni ou était incorrect
|
error.pdfPassword=Le document PDF est protégé par un mot de passe et le mot de passe n\u2019a pas été fourni ou était incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Ouvrir dans la même fenêtre
|
|||||||
settings.downloadOption.2=Ouvrir dans une nouvelle fenêtre
|
settings.downloadOption.2=Ouvrir dans une nouvelle fenêtre
|
||||||
settings.downloadOption.3=Télécharger le fichier
|
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.zipThreshold=Compresser les fichiers en ZIP lorsque le nombre de fichiers téléchargés dépasse
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -278,6 +329,15 @@ pdfToSinglePage.header=Fusionner des pages
|
|||||||
pdfToSinglePage.submit=Convertir en une seule page
|
pdfToSinglePage.submit=Convertir en une seule page
|
||||||
|
|
||||||
|
|
||||||
|
#pageExtracter
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
pageExtracter.title=Extract Pages
|
||||||
|
pageExtracter.header=Extract Pages
|
||||||
|
pageExtracter.submit=Extract
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
getPdfInfo.title=Récupérer les informations
|
getPdfInfo.title=Récupérer les informations
|
||||||
getPdfInfo.header=Récupérer les informations
|
getPdfInfo.header=Récupérer les informations
|
||||||
@ -293,6 +353,7 @@ MarkdownToPDF.help=(Travail en cours).
|
|||||||
MarkdownToPDF.credit=Utilise WeasyPrint.
|
MarkdownToPDF.credit=Utilise WeasyPrint.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL en PDF
|
URLToPDF.title=URL en PDF
|
||||||
URLToPDF.header=URL en PDF
|
URLToPDF.header=URL en PDF
|
||||||
@ -513,6 +574,11 @@ addImage.submit=Ajouter une image
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Fusionner
|
merge.title=Fusionner
|
||||||
merge.header=Fusionner plusieurs PDF
|
merge.header=Fusionner plusieurs PDF
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Fusionner
|
merge.submit=Fusionner
|
||||||
|
|
||||||
|
|
||||||
@ -527,6 +593,16 @@ multiTool.title=Outil multifonction PDF
|
|||||||
multiTool.header=Outil multifonction PDF
|
multiTool.header=Outil multifonction PDF
|
||||||
|
|
||||||
|
|
||||||
|
#pageRemover
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
pageRemover.title=Page Remover
|
||||||
|
pageRemover.header=PDF Page remover
|
||||||
|
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
|
||||||
|
pageRemover.submit=Delete Pages
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
rotate.title=Pivoter
|
rotate.title=Pivoter
|
||||||
rotate.header=Pivoter
|
rotate.header=Pivoter
|
||||||
@ -534,7 +610,7 @@ rotate.selectAngle=Angle de rotation (par multiples de 90\u202fdegrés)
|
|||||||
rotate.submit=Pivoter
|
rotate.submit=Pivoter
|
||||||
|
|
||||||
|
|
||||||
#split
|
#merge
|
||||||
split.title=Diviser
|
split.title=Diviser
|
||||||
split.header=Diviser
|
split.header=Diviser
|
||||||
split.desc.1=Les numéros que vous sélectionnez sont le numéro de page sur lequel vous souhaitez faire une division
|
split.desc.1=Les numéros que vous sélectionnez sont le numéro de page sur lequel vous souhaitez faire une division
|
||||||
@ -549,15 +625,15 @@ split.splitPages=Pages sur lesquelles diviser
|
|||||||
split.submit=Diviser
|
split.submit=Diviser
|
||||||
|
|
||||||
|
|
||||||
#imageToPDF
|
#merge
|
||||||
imageToPDF.title=Image en PDF
|
imageToPDF.title=Image en PDF
|
||||||
imageToPDF.header=Image en PDF
|
imageToPDF.header=Image en PDF
|
||||||
|
imageToPDF.submit=Convertir
|
||||||
imageToPDF.selectText.1=Étirer pour adapter
|
imageToPDF.selectText.1=Étirer pour adapter
|
||||||
imageToPDF.selectText.2=Rotation automatique du PDF
|
imageToPDF.selectText.2=Rotation automatique du PDF
|
||||||
imageToPDF.selectText.3=Logique multi-fichiers (uniquement activée si vous travaillez avec plusieurs images)
|
imageToPDF.selectText.3=Logique multi-fichiers (uniquement activée si vous travaillez avec plusieurs images)
|
||||||
imageToPDF.selectText.4=Fusionner en un seul PDF
|
imageToPDF.selectText.4=Fusionner en un seul PDF
|
||||||
imageToPDF.selectText.5=Convertir en PDF séparés
|
imageToPDF.selectText.5=Convertir en PDF séparés
|
||||||
imageToPDF.submit=Convertir
|
|
||||||
|
|
||||||
|
|
||||||
#pdfToImage
|
#pdfToImage
|
||||||
@ -637,7 +713,7 @@ removePassword.submit=Supprimer
|
|||||||
|
|
||||||
|
|
||||||
#changeMetadata
|
#changeMetadata
|
||||||
changeMetadata.title=Modifier les métadonnées
|
changeMetadata.title=Titre
|
||||||
changeMetadata.header=Modifier les métadonnées
|
changeMetadata.header=Modifier les métadonnées
|
||||||
changeMetadata.selectText.1=Veuillez modifier les variables que vous souhaitez modifier.
|
changeMetadata.selectText.1=Veuillez modifier les variables que vous souhaitez modifier.
|
||||||
changeMetadata.selectText.2=Supprimer toutes les métadonnées
|
changeMetadata.selectText.2=Supprimer toutes les métadonnées
|
||||||
@ -656,6 +732,16 @@ changeMetadata.selectText.5=Ajouter une entrée de métadonnées personnalisée
|
|||||||
changeMetadata.submit=Modifier
|
changeMetadata.submit=Modifier
|
||||||
|
|
||||||
|
|
||||||
|
#xlsToPdf
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
xlsToPdf.title=Excel to PDF
|
||||||
|
xlsToPdf.header=Excel to PDF
|
||||||
|
xlsToPdf.selectText.1=Select XLS or XLSX Excel sheet to convert
|
||||||
|
xlsToPdf.convert=convert
|
||||||
|
|
||||||
|
|
||||||
#pdfToPDFA
|
#pdfToPDFA
|
||||||
pdfToPDFA.title=PDF en PDF/A
|
pdfToPDFA.title=PDF en PDF/A
|
||||||
pdfToPDFA.header=PDF en PDF/A
|
pdfToPDFA.header=PDF en PDF/A
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Apri in questa finestra
|
|||||||
settings.downloadOption.2=Apri in una nuova finestra
|
settings.downloadOption.2=Apri in una nuova finestra
|
||||||
settings.downloadOption.3=Scarica file
|
settings.downloadOption.3=Scarica file
|
||||||
settings.zipThreshold=Comprimi file in .zip quando il numero di download supera
|
settings.zipThreshold=Comprimi file in .zip quando il numero di download supera
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Aggiungi immagine
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Unisci
|
merge.title=Unisci
|
||||||
merge.header=Unisci 2 o più PDF
|
merge.header=Unisci 2 o più PDF
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Unisci
|
merge.submit=Unisci
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Rotazione (0-360):
|
|||||||
watermark.selectText.5=spazio orizzontale (tra ogni filigrana):
|
watermark.selectText.5=spazio orizzontale (tra ogni filigrana):
|
||||||
watermark.selectText.6=spazio verticale (tra ogni filigrana):
|
watermark.selectText.6=spazio verticale (tra ogni filigrana):
|
||||||
watermark.selectText.7=Opacità (0% - 100%):
|
watermark.selectText.7=Opacità (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Aggiungi Filigrana
|
watermark.submit=Aggiungi Filigrana
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Rimuovi Filigrana
|
|
||||||
remove-watermark.header=Rimuovi filigrana
|
|
||||||
remove-watermark.selectText.1=Seleziona PDF da cui rimuovere la filigrana:
|
|
||||||
remove-watermark.selectText.2=Testo:
|
|
||||||
remove-watermark.submit=Rimuovi Filigrana
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Cambia Permessi
|
permissions.title=Cambia Permessi
|
||||||
permissions.header=Cambia permessi
|
permissions.header=Cambia permessi
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=PDFにパスワードが設定されてますが、パスワードが入力されてないか間違ってます。
|
error.pdfPassword=PDFにパスワードが設定されてますが、パスワードが入力されてないか間違ってます。
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=同じウィンドウで開く
|
|||||||
settings.downloadOption.2=新しいウィンドウで開く
|
settings.downloadOption.2=新しいウィンドウで開く
|
||||||
settings.downloadOption.3=ファイルをダウンロード
|
settings.downloadOption.3=ファイルをダウンロード
|
||||||
settings.zipThreshold=このファイル数を超えたときにファイルを圧縮する
|
settings.zipThreshold=このファイル数を超えたときにファイルを圧縮する
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=画像の追加
|
|||||||
#merge
|
#merge
|
||||||
merge.title=結合
|
merge.title=結合
|
||||||
merge.header=複数のPDFを結合 (2ファイル以上)
|
merge.header=複数のPDFを結合 (2ファイル以上)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=結合
|
merge.submit=結合
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=回転 (0-360):
|
|||||||
watermark.selectText.5=幅スペース (各透かし間の水平方向のスペース):
|
watermark.selectText.5=幅スペース (各透かし間の水平方向のスペース):
|
||||||
watermark.selectText.6=高さスペース (各透かし間の垂直方向のスペース):
|
watermark.selectText.6=高さスペース (各透かし間の垂直方向のスペース):
|
||||||
watermark.selectText.7=不透明度 (0% - 100%):
|
watermark.selectText.7=不透明度 (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=透かしを追加
|
watermark.submit=透かしを追加
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=透かしの削除
|
|
||||||
remove-watermark.header=透かしの削除
|
|
||||||
remove-watermark.selectText.1=透かしを削除するPDFを選択:
|
|
||||||
remove-watermark.selectText.2=透かしのテキスト:
|
|
||||||
remove-watermark.submit=透かしを削除
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=権限の変更
|
permissions.title=権限の変更
|
||||||
permissions.header=権限の変更
|
permissions.header=権限の変更
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=현재 창에서 열기
|
|||||||
settings.downloadOption.2=새 창에서 열기
|
settings.downloadOption.2=새 창에서 열기
|
||||||
settings.downloadOption.3=다운로드
|
settings.downloadOption.3=다운로드
|
||||||
settings.zipThreshold=다운로드한 파일 수가 초과된 경우 파일 압축하기
|
settings.zipThreshold=다운로드한 파일 수가 초과된 경우 파일 압축하기
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=이미지 추가
|
|||||||
#merge
|
#merge
|
||||||
merge.title=병합
|
merge.title=병합
|
||||||
merge.header=여러 개의 PDF 병합 (2개 이상)
|
merge.header=여러 개의 PDF 병합 (2개 이상)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=병합
|
merge.submit=병합
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=회전 각도 (0-360):
|
|||||||
watermark.selectText.5=가로 간격 (각 워터마크 사이의 가로 공간):
|
watermark.selectText.5=가로 간격 (각 워터마크 사이의 가로 공간):
|
||||||
watermark.selectText.6=세로 간격 (각 워터마크 사이의 세로 공간):
|
watermark.selectText.6=세로 간격 (각 워터마크 사이의 세로 공간):
|
||||||
watermark.selectText.7=투명도 (0% - 100%):
|
watermark.selectText.7=투명도 (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=워터마크 추가
|
watermark.submit=워터마크 추가
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=워터마크 제거
|
|
||||||
remove-watermark.header=워터마크 제거
|
|
||||||
remove-watermark.selectText.1=워터마크를 제거할 PDF 선택:
|
|
||||||
remove-watermark.selectText.2=워터마크 텍스트:
|
|
||||||
remove-watermark.submit=워터마크 제거
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=권한 변경
|
permissions.title=권한 변경
|
||||||
permissions.header=권한 변경
|
permissions.header=권한 변경
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=Dokument PDF jest zabezpieczony hasłem, musisz podać prawidłowe hasło.
|
error.pdfPassword=Dokument PDF jest zabezpieczony hasłem, musisz podać prawidłowe hasło.
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Otwórz w tym samym oknie
|
|||||||
settings.downloadOption.2=Otwórz w nowym oknie
|
settings.downloadOption.2=Otwórz w nowym oknie
|
||||||
settings.downloadOption.3=Pobierz plik
|
settings.downloadOption.3=Pobierz plik
|
||||||
settings.zipThreshold=Spakuj pliki, gdy liczba pobranych plików przekroczy
|
settings.zipThreshold=Spakuj pliki, gdy liczba pobranych plików przekroczy
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Dodaj obraz
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Połącz
|
merge.title=Połącz
|
||||||
merge.header=Połącz wiele dokumentów PDF (2+)
|
merge.header=Połącz wiele dokumentów PDF (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Połącz
|
merge.submit=Połącz
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Obrót (0-360):
|
|||||||
watermark.selectText.5=Odstęp w poziomie (odstęp między każdym znakiem wodnym w poziomie):
|
watermark.selectText.5=Odstęp w poziomie (odstęp między każdym znakiem wodnym w poziomie):
|
||||||
watermark.selectText.6=Odstęp w pionie (odstęp między każdym znakiem wodnym w pionie):
|
watermark.selectText.6=Odstęp w pionie (odstęp między każdym znakiem wodnym w pionie):
|
||||||
watermark.selectText.7=Nieprzezroczystość (0% - 100%):
|
watermark.selectText.7=Nieprzezroczystość (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Dodaj znak wodny
|
watermark.submit=Dodaj znak wodny
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Usuń znak wodny
|
|
||||||
remove-watermark.header=Usuń znak wodny
|
|
||||||
remove-watermark.selectText.1=Wybierz dokument PDF, aby usunąć znak wodny z:
|
|
||||||
remove-watermark.selectText.2=Treść zanku wodnego:
|
|
||||||
remove-watermark.submit=Usuń znak wodny
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Zmień uprawnienia
|
permissions.title=Zmień uprawnienia
|
||||||
permissions.header=Zmień uprawnienia
|
permissions.header=Zmień uprawnienia
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Médio
|
|||||||
sizes.large=Grande
|
sizes.large=Grande
|
||||||
sizes.x-large=Muito grande
|
sizes.x-large=Muito grande
|
||||||
error.pdfPassword=O documento PDF está protegido por senha e a senha não foi fornecida ou está incorreta
|
error.pdfPassword=O documento PDF está protegido por senha e a senha não foi fornecida ou está incorreta
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,12 +61,57 @@ settings.downloadOption.1=Abrir na mesma janela
|
|||||||
settings.downloadOption.2=Abrir em nova janela
|
settings.downloadOption.2=Abrir em nova janela
|
||||||
settings.downloadOption.3=⇬ Fazer download do arquivo
|
settings.downloadOption.3=⇬ Fazer download do arquivo
|
||||||
settings.zipThreshold=Compactar arquivos quando o número de arquivos baixados exceder
|
settings.zipThreshold=Compactar arquivos quando o número de arquivos baixados exceder
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
#############
|
#############
|
||||||
home.desc=Seu melhor utilitário para suas necessidades de PDF.
|
home.desc=Seu melhor utilitário para suas necessidades de PDF.
|
||||||
|
|
||||||
|
|
||||||
home.multiTool.title=Multiferramenta de PDF
|
home.multiTool.title=Multiferramenta de PDF
|
||||||
home.multiTool.desc=Mesclar, girar, reorganizar e remover páginas
|
home.multiTool.desc=Mesclar, girar, reorganizar e remover páginas
|
||||||
multiTool.tags=Multi Ferramenta, Operação Múltipla, Interface do Usuário, Clique e Arraste, Front-end, Lado do Cliente
|
multiTool.tags=Multi Ferramenta, Operação Múltipla, Interface do Usuário, Clique e Arraste, Front-end, Lado do Cliente
|
||||||
@ -76,6 +128,7 @@ home.rotate.title=Girar
|
|||||||
home.rotate.desc=Girar facilmente seus PDFs.
|
home.rotate.desc=Girar facilmente seus PDFs.
|
||||||
rotate.tags=Lado do Servidor
|
rotate.tags=Lado do Servidor
|
||||||
|
|
||||||
|
|
||||||
home.imageToPdf.title=Imagem para PDF
|
home.imageToPdf.title=Imagem para PDF
|
||||||
home.imageToPdf.desc=Converter uma imagem (PNG, JPEG, GIF) em PDF.
|
home.imageToPdf.desc=Converter uma imagem (PNG, JPEG, GIF) em PDF.
|
||||||
imageToPdf.tags=conversão, img, jpg, imagem, foto
|
imageToPdf.tags=conversão, img, jpg, imagem, foto
|
||||||
@ -88,6 +141,7 @@ home.pdfOrganiser.title=Organizar
|
|||||||
home.pdfOrganiser.desc=Remover/reorganizar as páginas em qualquer ordem.
|
home.pdfOrganiser.desc=Remover/reorganizar as páginas em qualquer ordem.
|
||||||
pdfOrganiser.tags=duplex, par, ímpar, ordenar, mover
|
pdfOrganiser.tags=duplex, par, ímpar, ordenar, mover
|
||||||
|
|
||||||
|
|
||||||
home.addImage.title=Adicionar Imagem
|
home.addImage.title=Adicionar Imagem
|
||||||
home.addImage.desc=Adicionar uma imagem em um local definido no PDF (trabalho em andamento)
|
home.addImage.desc=Adicionar uma imagem em um local definido no PDF (trabalho em andamento)
|
||||||
addImage.tags=img, jpg, imagem, foto
|
addImage.tags=img, jpg, imagem, foto
|
||||||
@ -100,6 +154,7 @@ home.permissions.title=Alterar Permissões
|
|||||||
home.permissions.desc=Alterar as permissões do seu documento PDF.
|
home.permissions.desc=Alterar as permissões do seu documento PDF.
|
||||||
permissions.tags=leitura, escrita, edição, impressão
|
permissions.tags=leitura, escrita, edição, impressão
|
||||||
|
|
||||||
|
|
||||||
home.removePages.title=Remover
|
home.removePages.title=Remover
|
||||||
home.removePages.desc=Excluir as páginas indesejadas do seu documento PDF.
|
home.removePages.desc=Excluir as páginas indesejadas do seu documento PDF.
|
||||||
removePages.tags=Remover páginas, excluir páginas
|
removePages.tags=Remover páginas, excluir páginas
|
||||||
@ -116,6 +171,7 @@ home.compressPdfs.title=Comprimir
|
|||||||
home.compressPdfs.desc=Comprimir PDFs para reduzir o tamanho do arquivo.
|
home.compressPdfs.desc=Comprimir PDFs para reduzir o tamanho do arquivo.
|
||||||
compressPdfs.tags=compactar, pequeno, mínimo
|
compressPdfs.tags=compactar, pequeno, mínimo
|
||||||
|
|
||||||
|
|
||||||
home.changeMetadata.title=Alterar Metadados
|
home.changeMetadata.title=Alterar Metadados
|
||||||
home.changeMetadata.desc=Alterar/remover/adicionar metadados de um documento PDF.
|
home.changeMetadata.desc=Alterar/remover/adicionar metadados de um documento PDF.
|
||||||
changeMetadata.tags=Título, autor, data, criação, hora, editor, produtor, estatísticas
|
changeMetadata.tags=Título, autor, data, criação, hora, editor, produtor, estatísticas
|
||||||
@ -128,6 +184,7 @@ home.ocr.title=OCR / Limpeza de Digitalizações
|
|||||||
home.ocr.desc=A limpeza verifica e detecta texto em imagens de um PDF e o adiciona novamente como texto.
|
home.ocr.desc=A limpeza verifica e detecta texto em imagens de um PDF e o adiciona novamente como texto.
|
||||||
ocr.tags=reconhecimento, texto, imagem, digitalização, leitura, identificação, detecção, editável
|
ocr.tags=reconhecimento, texto, imagem, digitalização, leitura, identificação, detecção, editável
|
||||||
|
|
||||||
|
|
||||||
home.extractImages.title=Extrair Imagens
|
home.extractImages.title=Extrair Imagens
|
||||||
home.extractImages.desc=Extrair todas as imagens de um PDF e salvá-las em um arquivo zip.
|
home.extractImages.desc=Extrair todas as imagens de um PDF e salvá-las em um arquivo zip.
|
||||||
extractImages.tags=imagem, foto, salvar, arquivo, zip, captura, coleta
|
extractImages.tags=imagem, foto, salvar, arquivo, zip, captura, coleta
|
||||||
@ -152,6 +209,7 @@ home.PDFToHTML.title=PDF para HTML
|
|||||||
home.PDFToHTML.desc=Converter PDF para o formato HTML
|
home.PDFToHTML.desc=Converter PDF para o formato HTML
|
||||||
PDFToHTML.tags=conteúdo web, compatível com navegador
|
PDFToHTML.tags=conteúdo web, compatível com navegador
|
||||||
|
|
||||||
|
|
||||||
home.PDFToXML.title=PDF para XML
|
home.PDFToXML.title=PDF para XML
|
||||||
home.PDFToXML.desc=Converter PDF para o formato XML
|
home.PDFToXML.desc=Converter PDF para o formato XML
|
||||||
PDFToXML.tags=extração-de-dados,conteúdo-estruturado,interoperabilidade,transformação,converter
|
PDFToXML.tags=extração-de-dados,conteúdo-estruturado,interoperabilidade,transformação,converter
|
||||||
@ -228,26 +286,27 @@ home.HTMLToPDF.title=HTML para PDF
|
|||||||
home.HTMLToPDF.desc=Converte qualquer arquivo HTML ou zip para PDF
|
home.HTMLToPDF.desc=Converte qualquer arquivo HTML ou zip para PDF
|
||||||
HTMLToPDF.tags=marcação,conteúdo-web,transformação,converter
|
HTMLToPDF.tags=marcação,conteúdo-web,transformação,converter
|
||||||
|
|
||||||
|
|
||||||
home.MarkdownToPDF.title=Markdown para PDF
|
home.MarkdownToPDF.title=Markdown para PDF
|
||||||
home.MarkdownToPDF.desc=Converte qualquer arquivo Markdown para PDF
|
home.MarkdownToPDF.desc=Converte qualquer arquivo Markdown para PDF
|
||||||
MarkdownToPDF.tags=marcação,conteúdo-web,transformação,converter
|
MarkdownToPDF.tags=marcação,conteúdo-web,transformação,converter
|
||||||
|
|
||||||
|
|
||||||
home.getPdfInfo.title=Obter TODAS as Informações de um PDF
|
home.getPdfInfo.title=Obter TODAS as Informações de um PDF
|
||||||
home.getPdfInfo.desc=Obtém todas as informações possíveis de um PDF
|
home.getPdfInfo.desc=Obtém todas as informações possíveis de um PDF
|
||||||
getPdfInfo.tags=informações,dados,estatísticas
|
getPdfInfo.tags=informações,dados,estatísticas
|
||||||
|
|
||||||
|
|
||||||
home.extractPage.title=Extrair Página(s)
|
home.extractPage.title=Extrair Página(s)
|
||||||
home.extractPage.desc=Extrai páginas selecionadas de um PDF
|
home.extractPage.desc=Extrai páginas selecionadas de um PDF
|
||||||
extractPage.tags=extrair
|
extractPage.tags=extrair
|
||||||
|
|
||||||
|
|
||||||
home.PdfToSinglePage.title=PDF para Página Única Grande
|
home.PdfToSinglePage.title=PDF para Página Única Grande
|
||||||
home.PdfToSinglePage.desc=Combina todas as páginas de um PDF em uma única página grande
|
home.PdfToSinglePage.desc=Combina todas as páginas de um PDF em uma única página grande
|
||||||
PdfToSinglePage.tags=página única
|
PdfToSinglePage.tags=página única
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Mostrar Javascript
|
home.showJS.title=Mostrar Javascript
|
||||||
home.showJS.desc=Procura e exibe qualquer JavaScript injetado em um PDF
|
home.showJS.desc=Procura e exibe qualquer JavaScript injetado em um PDF
|
||||||
showJS.tags=JavaScript
|
showJS.tags=JavaScript
|
||||||
@ -258,30 +317,31 @@ showJS.tags=JavaScript
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Exibir JavaScript
|
showJS.title=Exibir JavaScript
|
||||||
showJS.header=Exibir JavaScript
|
showJS.header=Exibir JavaScript
|
||||||
showJS.downloadJS=Download do JavaScript
|
showJS.downloadJS=Download do JavaScript
|
||||||
showJS.submit=Exibir
|
showJS.submit=Exibir
|
||||||
|
|
||||||
|
|
||||||
#pdfToSinglePage
|
#pdfToSinglePage
|
||||||
pdfToSinglePage.title=PDF para Página Única
|
pdfToSinglePage.title=PDF para Página Única
|
||||||
pdfToSinglePage.header=PDF para Página Única
|
pdfToSinglePage.header=PDF para Página Única
|
||||||
pdfToSinglePage.submit=Converter para Página Única
|
pdfToSinglePage.submit=Converter para Página Única
|
||||||
|
|
||||||
|
|
||||||
#pageExtracter
|
#pageExtracter
|
||||||
pageExtracter.title=Extrair Páginas
|
pageExtracter.title=Extrair Páginas
|
||||||
pageExtracter.header=Extrair Páginas
|
pageExtracter.header=Extrair Páginas
|
||||||
pageExtracter.submit=Extrair
|
pageExtracter.submit=Extrair
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
getPdfInfo.title=Obter Informações do PDF
|
getPdfInfo.title=Obter Informações do PDF
|
||||||
getPdfInfo.header=Obter Informações do PDF
|
getPdfInfo.header=Obter Informações do PDF
|
||||||
getPdfInfo.submit=Obter Informações
|
getPdfInfo.submit=Obter Informações
|
||||||
getPdfInfo.downloadJson=Download JSON
|
getPdfInfo.downloadJson=Download JSON
|
||||||
|
|
||||||
|
|
||||||
#markdown-to-pdf
|
#markdown-to-pdf
|
||||||
MarkdownToPDF.title=Markdown para PDF
|
MarkdownToPDF.title=Markdown para PDF
|
||||||
MarkdownToPDF.header=Markdown para PDF
|
MarkdownToPDF.header=Markdown para PDF
|
||||||
@ -289,12 +349,15 @@ MarkdownToPDF.submit=Converter
|
|||||||
MarkdownToPDF.help=Trabalho em andamento
|
MarkdownToPDF.help=Trabalho em andamento
|
||||||
MarkdownToPDF.credit=Usa o WeasyPrint
|
MarkdownToPDF.credit=Usa o WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#url-to-pdf
|
#url-to-pdf
|
||||||
URLToPDF.title=URL para PDF
|
URLToPDF.title=URL para PDF
|
||||||
URLToPDF.header=URL para PDF
|
URLToPDF.header=URL para PDF
|
||||||
URLToPDF.submit=Converter
|
URLToPDF.submit=Converter
|
||||||
URLToPDF.credit=Usa o WeasyPrint
|
URLToPDF.credit=Usa o WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
#html-to-pdf
|
#html-to-pdf
|
||||||
HTMLToPDF.title=HTML para PDF
|
HTMLToPDF.title=HTML para PDF
|
||||||
HTMLToPDF.header=HTML para PDF
|
HTMLToPDF.header=HTML para PDF
|
||||||
@ -302,6 +365,7 @@ HTMLToPDF.help=Aceita arquivos HTML e ZIPs contendo html/css/imagens etc necess
|
|||||||
HTMLToPDF.submit=Converter
|
HTMLToPDF.submit=Converter
|
||||||
HTMLToPDF.credit=Usa o WeasyPrint
|
HTMLToPDF.credit=Usa o WeasyPrint
|
||||||
|
|
||||||
|
|
||||||
#sanitizePDF
|
#sanitizePDF
|
||||||
sanitizePDF.title=Sanitizar PDF
|
sanitizePDF.title=Sanitizar PDF
|
||||||
sanitizePDF.header=Sanitizar um arquivo PDF
|
sanitizePDF.header=Sanitizar um arquivo PDF
|
||||||
@ -312,8 +376,8 @@ sanitizePDF.selectText.4=Remover links
|
|||||||
sanitizePDF.selectText.5=Remover fontes
|
sanitizePDF.selectText.5=Remover fontes
|
||||||
sanitizePDF.submit=Sanitizar PDF
|
sanitizePDF.submit=Sanitizar PDF
|
||||||
|
|
||||||
|
|
||||||
#addPageNumbers
|
#addPageNumbers
|
||||||
autoCrop.title=Adicionar Números de Página
|
|
||||||
addPageNumbers.title=Adicionar Números de Página
|
addPageNumbers.title=Adicionar Números de Página
|
||||||
addPageNumbers.header=Adicionar Números de Página
|
addPageNumbers.header=Adicionar Números de Página
|
||||||
addPageNumbers.selectText.1=Selecionar arquivo PDF:
|
addPageNumbers.selectText.1=Selecionar arquivo PDF:
|
||||||
@ -324,11 +388,13 @@ addPageNumbers.selectText.5=Páginas a Numerar
|
|||||||
addPageNumbers.selectText.6=Texto Personalizado
|
addPageNumbers.selectText.6=Texto Personalizado
|
||||||
addPageNumbers.submit=Adicionar Números de Página
|
addPageNumbers.submit=Adicionar Números de Página
|
||||||
|
|
||||||
|
|
||||||
#auto-rename
|
#auto-rename
|
||||||
auto-rename.title=Rename Automático
|
auto-rename.title=Rename Automático
|
||||||
auto-rename.header=Rename Automático de PDF
|
auto-rename.header=Rename Automático de PDF
|
||||||
auto-rename.submit=Rename Automático
|
auto-rename.submit=Rename Automático
|
||||||
|
|
||||||
|
|
||||||
#adjustContrast
|
#adjustContrast
|
||||||
adjustContrast.title=Ajustar Contraste
|
adjustContrast.title=Ajustar Contraste
|
||||||
adjustContrast.header=Ajustar Contraste
|
adjustContrast.header=Ajustar Contraste
|
||||||
@ -337,11 +403,13 @@ adjustContrast.brightness=Brilho:
|
|||||||
adjustContrast.saturation=Saturação:
|
adjustContrast.saturation=Saturação:
|
||||||
adjustContrast.download=Download
|
adjustContrast.download=Download
|
||||||
|
|
||||||
|
|
||||||
#crop
|
#crop
|
||||||
crop.title=Cortar
|
crop.title=Cortar
|
||||||
crop.header=Cortar Imagem
|
crop.header=Cortar Imagem
|
||||||
crop.submit=Enviar
|
crop.submit=Enviar
|
||||||
|
|
||||||
|
|
||||||
#autoSplitPDF
|
#autoSplitPDF
|
||||||
autoSplitPDF.title=Divisão Automática de PDF
|
autoSplitPDF.title=Divisão Automática de PDF
|
||||||
autoSplitPDF.header=Divisão Automática de PDF
|
autoSplitPDF.header=Divisão Automática de PDF
|
||||||
@ -356,6 +424,7 @@ autoSplitPDF.dividerDownload1=Download 'Folha Divisória Automática (mínima).p
|
|||||||
autoSplitPDF.dividerDownload2=Download 'Folha Divisória Automática (com instruções).pdf'
|
autoSplitPDF.dividerDownload2=Download 'Folha Divisória Automática (com instruções).pdf'
|
||||||
autoSplitPDF.submit=Enviar
|
autoSplitPDF.submit=Enviar
|
||||||
|
|
||||||
|
|
||||||
#pipeline
|
#pipeline
|
||||||
pipeline.title=Pipeline
|
pipeline.title=Pipeline
|
||||||
|
|
||||||
@ -366,6 +435,7 @@ pageLayout.header=Layout de Múltiplas Páginas
|
|||||||
pageLayout.pagesPerSheet=Páginas por folha:
|
pageLayout.pagesPerSheet=Páginas por folha:
|
||||||
pageLayout.submit=Enviar
|
pageLayout.submit=Enviar
|
||||||
|
|
||||||
|
|
||||||
#scalePages
|
#scalePages
|
||||||
scalePages.title=Ajustar Tamanho/Escala da Página
|
scalePages.title=Ajustar Tamanho/Escala da Página
|
||||||
scalePages.header=Ajustar Tamanho/Escala da Página
|
scalePages.header=Ajustar Tamanho/Escala da Página
|
||||||
@ -373,6 +443,7 @@ scalePages.pageSize=Tamanho de uma página do documento.
|
|||||||
scalePages.scaleFactor=Fator de zoom (corte) de uma página.
|
scalePages.scaleFactor=Fator de zoom (corte) de uma página.
|
||||||
scalePages.submit=Enviar
|
scalePages.submit=Enviar
|
||||||
|
|
||||||
|
|
||||||
#certSign
|
#certSign
|
||||||
certSign.title=Assinatura com Certificado
|
certSign.title=Assinatura com Certificado
|
||||||
certSign.header=Assine um PDF com o seu certificado (Em desenvolvimento)
|
certSign.header=Assine um PDF com o seu certificado (Em desenvolvimento)
|
||||||
@ -388,6 +459,7 @@ certSign.location=Localização
|
|||||||
certSign.name=Nome
|
certSign.name=Nome
|
||||||
certSign.submit=Assinar PDF
|
certSign.submit=Assinar PDF
|
||||||
|
|
||||||
|
|
||||||
#removeBlanks
|
#removeBlanks
|
||||||
removeBlanks.title=Remover Páginas em Branco
|
removeBlanks.title=Remover Páginas em Branco
|
||||||
removeBlanks.header=Remover Páginas em Branco
|
removeBlanks.header=Remover Páginas em Branco
|
||||||
@ -397,6 +469,7 @@ removeBlanks.whitePercent=Porcentagem de Branco (%):
|
|||||||
removeBlanks.whitePercentDesc=Porcentagem da página que deve ser branca para ser removida
|
removeBlanks.whitePercentDesc=Porcentagem da página que deve ser branca para ser removida
|
||||||
removeBlanks.submit=Remover Páginas em Branco
|
removeBlanks.submit=Remover Páginas em Branco
|
||||||
|
|
||||||
|
|
||||||
#compare
|
#compare
|
||||||
compare.title=Comparar
|
compare.title=Comparar
|
||||||
compare.header=Comparar PDFs
|
compare.header=Comparar PDFs
|
||||||
@ -404,6 +477,7 @@ compare.document.1=Documento 1
|
|||||||
compare.document.2=Documento 2
|
compare.document.2=Documento 2
|
||||||
compare.submit=Comparar
|
compare.submit=Comparar
|
||||||
|
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Assinar
|
sign.title=Assinar
|
||||||
sign.header=Assinar PDFs
|
sign.header=Assinar PDFs
|
||||||
@ -413,16 +487,19 @@ sign.text=Inserir Texto
|
|||||||
sign.clear=Limpar
|
sign.clear=Limpar
|
||||||
sign.add=Adicionar
|
sign.add=Adicionar
|
||||||
|
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Reparar PDFs
|
repair.header=Reparar PDFs
|
||||||
repair.submit=Reparar
|
repair.submit=Reparar
|
||||||
|
|
||||||
|
|
||||||
#flatten
|
#flatten
|
||||||
flatten.title=Achatar
|
flatten.title=Achatar
|
||||||
flatten.header=Achatar PDFs
|
flatten.header=Achatar PDFs
|
||||||
flatten.submit=Achatar
|
flatten.submit=Achatar
|
||||||
|
|
||||||
|
|
||||||
#ScannerImageSplit
|
#ScannerImageSplit
|
||||||
ScannerImageSplit.selectText.1=Limite de Ângulo:
|
ScannerImageSplit.selectText.1=Limite de Ângulo:
|
||||||
ScannerImageSplit.selectText.2=Define o ângulo absoluto mínimo necessário para que a imagem seja girada (padrão: 10).
|
ScannerImageSplit.selectText.2=Define o ângulo absoluto mínimo necessário para que a imagem seja girada (padrão: 10).
|
||||||
@ -450,16 +527,22 @@ ocr.selectText.8=Normal (gerará um erro se o PDF já contiver texto)
|
|||||||
ocr.selectText.9=Configurações adicionais
|
ocr.selectText.9=Configurações adicionais
|
||||||
ocr.selectText.10=Modo OCR
|
ocr.selectText.10=Modo OCR
|
||||||
ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão)
|
ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
ocr.selectText.12=Render Type (Advanced)
|
||||||
ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker
|
ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker
|
||||||
ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR.
|
ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR.
|
||||||
ocr.submit=Processar PDF com OCR
|
ocr.submit=Processar PDF com OCR
|
||||||
|
|
||||||
|
|
||||||
#extractImages
|
#extractImages
|
||||||
extractImages.title=Extrair Imagens
|
extractImages.title=Extrair Imagens
|
||||||
extractImages.header=Extrair Imagens
|
extractImages.header=Extrair Imagens
|
||||||
extractImages.selectText=Selecione o formato de imagem para converter as imagens extraídas
|
extractImages.selectText=Selecione o formato de imagem para converter as imagens extraídas
|
||||||
extractImages.submit=Extrair
|
extractImages.submit=Extrair
|
||||||
|
|
||||||
|
|
||||||
#File to PDF
|
#File to PDF
|
||||||
fileToPDF.title=Arquivo para PDF
|
fileToPDF.title=Arquivo para PDF
|
||||||
fileToPDF.header=Converter Qualquer Arquivo para PDF
|
fileToPDF.header=Converter Qualquer Arquivo para PDF
|
||||||
@ -467,6 +550,7 @@ fileToPDF.credit=Este serviço usa o LibreOffice e o Unoconv para conversão de
|
|||||||
fileToPDF.supportedFileTypes=Os tipos de arquivo suportados devem incluir os listados abaixo. No entanto, para obter uma lista atualizada completa dos formatos suportados, consulte a documentação do LibreOffice.
|
fileToPDF.supportedFileTypes=Os tipos de arquivo suportados devem incluir os listados abaixo. No entanto, para obter uma lista atualizada completa dos formatos suportados, consulte a documentação do LibreOffice.
|
||||||
fileToPDF.submit=Converter para PDF
|
fileToPDF.submit=Converter para PDF
|
||||||
|
|
||||||
|
|
||||||
#compress
|
#compress
|
||||||
compress.title=Comprimir
|
compress.title=Comprimir
|
||||||
compress.header=Comprimir PDF
|
compress.header=Comprimir PDF
|
||||||
@ -478,6 +562,7 @@ compress.selectText.4=Modo Automático - Ajusta automaticamente a qualidade para
|
|||||||
compress.selectText.5=Tamanho Esperado do PDF (por exemplo, 25 MB, 10,8 MB, 25 KB)
|
compress.selectText.5=Tamanho Esperado do PDF (por exemplo, 25 MB, 10,8 MB, 25 KB)
|
||||||
compress.submit=Comprimir
|
compress.submit=Comprimir
|
||||||
|
|
||||||
|
|
||||||
#Add image
|
#Add image
|
||||||
addImage.title=Adicionar Imagem
|
addImage.title=Adicionar Imagem
|
||||||
addImage.header=Adicionar Imagem ao PDF
|
addImage.header=Adicionar Imagem ao PDF
|
||||||
@ -485,32 +570,43 @@ addImage.everyPage=Para cada página?
|
|||||||
addImage.upload=Enviar Imagem
|
addImage.upload=Enviar Imagem
|
||||||
addImage.submit=Adicionar Imagem
|
addImage.submit=Adicionar Imagem
|
||||||
|
|
||||||
|
|
||||||
#merge
|
#merge
|
||||||
merge.title=Mesclar
|
merge.title=Mesclar
|
||||||
merge.header=Mesclar Vários PDFs (2+)
|
merge.header=Mesclar Vários PDFs (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Mesclar
|
merge.submit=Mesclar
|
||||||
|
|
||||||
|
|
||||||
#pdfOrganiser
|
#pdfOrganiser
|
||||||
pdfOrganiser.title=Organizador de Páginas
|
pdfOrganiser.title=Organizador de Páginas
|
||||||
pdfOrganiser.header=Organizador de Páginas PDF
|
pdfOrganiser.header=Organizador de Páginas PDF
|
||||||
pdfOrganiser.submit=Reorganizar Páginas
|
pdfOrganiser.submit=Reorganizar Páginas
|
||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Multiferramenta de PDF
|
multiTool.title=Multiferramenta de PDF
|
||||||
multiTool.header=Multiferramenta de PDF
|
multiTool.header=Multiferramenta de PDF
|
||||||
|
|
||||||
|
|
||||||
#pageRemover
|
#pageRemover
|
||||||
pageRemover.title=Remover Página
|
pageRemover.title=Remover Página
|
||||||
pageRemover.header=Remover Páginas do PDF
|
pageRemover.header=Remover Páginas do PDF
|
||||||
pageRemover.pagesToDelete=Páginas a serem excluídas (insira uma lista separada por vírgulas de números de página):
|
pageRemover.pagesToDelete=Páginas a serem excluídas (insira uma lista separada por vírgulas de números de página):
|
||||||
pageRemover.submit=Excluir Páginas
|
pageRemover.submit=Excluir Páginas
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
rotate.title=Girar PDF
|
rotate.title=Girar PDF
|
||||||
rotate.header=Girar PDF
|
rotate.header=Girar PDF
|
||||||
rotate.selectAngle=Selecione o ângulo de rotação (múltiplos de 90 graus):
|
rotate.selectAngle=Selecione o ângulo de rotação (múltiplos de 90 graus):
|
||||||
rotate.submit=Girar
|
rotate.submit=Girar
|
||||||
|
|
||||||
|
|
||||||
#merge
|
#merge
|
||||||
split.title=Dividir PDF
|
split.title=Dividir PDF
|
||||||
split.header=Dividir PDF
|
split.header=Dividir PDF
|
||||||
@ -525,6 +621,7 @@ split.desc.8=Documento Nº6: Páginas 9 e 10
|
|||||||
split.splitPages=Digite as páginas para a divisão:
|
split.splitPages=Digite as páginas para a divisão:
|
||||||
split.submit=Dividir
|
split.submit=Dividir
|
||||||
|
|
||||||
|
|
||||||
#merge
|
#merge
|
||||||
imageToPDF.title=Imagem para PDF
|
imageToPDF.title=Imagem para PDF
|
||||||
imageToPDF.header=Converter Imagem para PDF
|
imageToPDF.header=Converter Imagem para PDF
|
||||||
@ -535,6 +632,7 @@ imageToPDF.selectText.3=Lógica de Vários Arquivos (Ativada apenas ao trabalhar
|
|||||||
imageToPDF.selectText.4=Mesclar em um Único PDF
|
imageToPDF.selectText.4=Mesclar em um Único PDF
|
||||||
imageToPDF.selectText.5=Converter em PDFs Separados
|
imageToPDF.selectText.5=Converter em PDFs Separados
|
||||||
|
|
||||||
|
|
||||||
#pdfToImage
|
#pdfToImage
|
||||||
pdfToImage.title=PDF para Imagem
|
pdfToImage.title=PDF para Imagem
|
||||||
pdfToImage.header=Converter PDF para Imagem
|
pdfToImage.header=Converter PDF para Imagem
|
||||||
@ -570,6 +668,7 @@ addPassword.selectText.15=Restringe o que pode ser feito com o documento após a
|
|||||||
addPassword.selectText.16=Restringe a abertura do próprio documento
|
addPassword.selectText.16=Restringe a abertura do próprio documento
|
||||||
addPassword.submit=Criptografar
|
addPassword.submit=Criptografar
|
||||||
|
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
watermark.title=Adicionar Marca d'Água
|
watermark.title=Adicionar Marca d'Água
|
||||||
watermark.header=Adicionar Marca d'Água
|
watermark.header=Adicionar Marca d'Água
|
||||||
@ -584,12 +683,6 @@ watermark.selectText.8=Tipo de Marca d'Água
|
|||||||
watermark.selectText.9=Imagem da Marca d'Água
|
watermark.selectText.9=Imagem da Marca d'Água
|
||||||
watermark.submit=Adicionar Marca d'Água
|
watermark.submit=Adicionar Marca d'Água
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Remover Marca d'Água
|
|
||||||
remove-watermark.header=Remover Marca d'Água
|
|
||||||
remove-watermark.selectText.1=Selecione o PDF para Remover a Marca d'Água
|
|
||||||
remove-watermark.selectText.2=Texto da Marca d'Água
|
|
||||||
remove-watermark.submit=Remover Marca d'Água
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Alterar Permissões
|
permissions.title=Alterar Permissões
|
||||||
@ -607,6 +700,7 @@ permissions.selectText.9=Impedir Impressão
|
|||||||
permissions.selectText.10=Impedir Impressão de Formatos Diferentes
|
permissions.selectText.10=Impedir Impressão de Formatos Diferentes
|
||||||
permissions.submit=Mudar
|
permissions.submit=Mudar
|
||||||
|
|
||||||
|
|
||||||
#remove password
|
#remove password
|
||||||
removePassword.title=Remover Senha
|
removePassword.title=Remover Senha
|
||||||
removePassword.header=Remover Senha (Descriptografar)
|
removePassword.header=Remover Senha (Descriptografar)
|
||||||
@ -616,7 +710,7 @@ removePassword.submit=Remover
|
|||||||
|
|
||||||
|
|
||||||
#changeMetadata
|
#changeMetadata
|
||||||
changeMetadata.title=Alterar Metadados
|
changeMetadata.title=Título:
|
||||||
changeMetadata.header=Alterar Metadados
|
changeMetadata.header=Alterar Metadados
|
||||||
changeMetadata.selectText.1=Edite as Variáveis que Deseja Alterar
|
changeMetadata.selectText.1=Edite as Variáveis que Deseja Alterar
|
||||||
changeMetadata.selectText.2=Excluir Todos os Metadados
|
changeMetadata.selectText.2=Excluir Todos os Metadados
|
||||||
@ -634,18 +728,21 @@ changeMetadata.selectText.4=Outros Metadados
|
|||||||
changeMetadata.selectText.5=Adicionar Entrada de Metadados Personalizados
|
changeMetadata.selectText.5=Adicionar Entrada de Metadados Personalizados
|
||||||
changeMetadata.submit=Mudar
|
changeMetadata.submit=Mudar
|
||||||
|
|
||||||
|
|
||||||
#xlsToPdf
|
#xlsToPdf
|
||||||
xlsToPdf.title=Excel para PDF
|
xlsToPdf.title=Excel para PDF
|
||||||
xlsToPdf.header=Excel para PDF
|
xlsToPdf.header=Excel para PDF
|
||||||
xlsToPdf.selectText.1=Selecione a Planilha Excel XLS ou XLSX para Converter
|
xlsToPdf.selectText.1=Selecione a Planilha Excel XLS ou XLSX para Converter
|
||||||
xlsToPdf.convert=Converter
|
xlsToPdf.convert=Converter
|
||||||
|
|
||||||
|
|
||||||
#pdfToPDFA
|
#pdfToPDFA
|
||||||
pdfToPDFA.title=PDF para PDF/A
|
pdfToPDFA.title=PDF para PDF/A
|
||||||
pdfToPDFA.header=PDF para PDF/A
|
pdfToPDFA.header=PDF para PDF/A
|
||||||
pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A
|
pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A
|
||||||
pdfToPDFA.submit=Converter
|
pdfToPDFA.submit=Converter
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
PDFToWord.title=PDF para Word
|
PDFToWord.title=PDF para Word
|
||||||
PDFToWord.header=PDF para Word
|
PDFToWord.header=PDF para Word
|
||||||
@ -653,6 +750,7 @@ PDFToWord.selectText.1=Formato do Arquivo de Saída
|
|||||||
PDFToWord.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
PDFToWord.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
||||||
PDFToWord.submit=Converter
|
PDFToWord.submit=Converter
|
||||||
|
|
||||||
|
|
||||||
#PDFToPresentation
|
#PDFToPresentation
|
||||||
PDFToPresentation.title=PDF para Apresentação
|
PDFToPresentation.title=PDF para Apresentação
|
||||||
PDFToPresentation.header=PDF para Apresentação
|
PDFToPresentation.header=PDF para Apresentação
|
||||||
@ -660,6 +758,7 @@ PDFToPresentation.selectText.1=Formato do Arquivo de Saída
|
|||||||
PDFToPresentation.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
PDFToPresentation.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
||||||
PDFToPresentation.submit=Converter
|
PDFToPresentation.submit=Converter
|
||||||
|
|
||||||
|
|
||||||
#PDFToText
|
#PDFToText
|
||||||
PDFToText.title=PDF para Texto/RTF
|
PDFToText.title=PDF para Texto/RTF
|
||||||
PDFToText.header=PDF para Texto/RTF
|
PDFToText.header=PDF para Texto/RTF
|
||||||
@ -667,12 +766,14 @@ PDFToText.selectText.1=Formato do Arquivo de Saída
|
|||||||
PDFToText.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
PDFToText.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
||||||
PDFToText.submit=Converter
|
PDFToText.submit=Converter
|
||||||
|
|
||||||
|
|
||||||
#PDFToHTML
|
#PDFToHTML
|
||||||
PDFToHTML.title=PDF para HTML
|
PDFToHTML.title=PDF para HTML
|
||||||
PDFToHTML.header=PDF para HTML
|
PDFToHTML.header=PDF para HTML
|
||||||
PDFToHTML.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
PDFToHTML.credit=Este serviço usa o LibreOffice para Conversão de Arquivos.
|
||||||
PDFToHTML.submit=Converter
|
PDFToHTML.submit=Converter
|
||||||
|
|
||||||
|
|
||||||
#PDFToXML
|
#PDFToXML
|
||||||
PDFToXML.title=PDF para XML
|
PDFToXML.title=PDF para XML
|
||||||
PDFToXML.header=PDF para XML
|
PDFToXML.header=PDF para XML
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Deschide în aceeași fereastră
|
|||||||
settings.downloadOption.2=Deschide într-o fereastră nouă
|
settings.downloadOption.2=Deschide într-o fereastră nouă
|
||||||
settings.downloadOption.3=Descarcă fișierul
|
settings.downloadOption.3=Descarcă fișierul
|
||||||
settings.zipThreshold=Împachetează fișierele când numărul de fișiere descărcate depășește
|
settings.zipThreshold=Împachetează fișierele când numărul de fișiere descărcate depășește
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Adăugare imagine
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Unire
|
merge.title=Unire
|
||||||
merge.header=Unirea mai multor PDF-uri (2+)
|
merge.header=Unirea mai multor PDF-uri (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Unire
|
merge.submit=Unire
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Rotire (0-360):
|
|||||||
watermark.selectText.5=widthSpacer (Spațiu între fiecare filigran pe orizontală):
|
watermark.selectText.5=widthSpacer (Spațiu între fiecare filigran pe orizontală):
|
||||||
watermark.selectText.6=heightSpacer (Spațiu între fiecare filigran pe verticală):
|
watermark.selectText.6=heightSpacer (Spațiu între fiecare filigran pe verticală):
|
||||||
watermark.selectText.7=Opacitate (0% - 100%):
|
watermark.selectText.7=Opacitate (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Adăugați Filigran
|
watermark.submit=Adăugați Filigran
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Eliminați Filigran
|
|
||||||
remove-watermark.header=Eliminați Filigran
|
|
||||||
remove-watermark.selectText.1=Selectați PDF-ul de la care să eliminați filigranul:
|
|
||||||
remove-watermark.selectText.2=Textul Filigranului:
|
|
||||||
remove-watermark.submit=Eliminați Filigran
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Schimbați Permisiunile
|
permissions.title=Schimbați Permisiunile
|
||||||
permissions.header=Schimbați Permisiunile
|
permissions.header=Schimbați Permisiunile
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Открыть в том же окне
|
|||||||
settings.downloadOption.2=Открыть в новом окне
|
settings.downloadOption.2=Открыть в новом окне
|
||||||
settings.downloadOption.3=Загрузить файл
|
settings.downloadOption.3=Загрузить файл
|
||||||
settings.zipThreshold=Zip-файлы, когда количество загруженных файлов превышает
|
settings.zipThreshold=Zip-файлы, когда количество загруженных файлов превышает
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Добавить изображение
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Объединить
|
merge.title=Объединить
|
||||||
merge.header=Объединение нескольких PDF-файлов (2+)
|
merge.header=Объединение нескольких PDF-файлов (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Объединить
|
merge.submit=Объединить
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Поворот (0-360):
|
|||||||
watermark.selectText.5=widthSpacer (пробел между каждым водяным знаком по горизонтали):
|
watermark.selectText.5=widthSpacer (пробел между каждым водяным знаком по горизонтали):
|
||||||
watermark.selectText.6=heightSpacer (пробел между каждым водяным знаком по вертикали):
|
watermark.selectText.6=heightSpacer (пробел между каждым водяным знаком по вертикали):
|
||||||
watermark.selectText.7=Непрозрачность (0% - 100%):
|
watermark.selectText.7=Непрозрачность (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Добавить водяной знак
|
watermark.submit=Добавить водяной знак
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Удалить водяной знак
|
|
||||||
remove-watermark.header=Удалить водяной знак
|
|
||||||
remove-watermark.selectText.1=Выберите PDF, чтобы удалить водяной знак из:
|
|
||||||
remove-watermark.selectText.2=Текст водяного знака:
|
|
||||||
remove-watermark.submit=Удалить водяной знак
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Изменить разрешения
|
permissions.title=Изменить разрешения
|
||||||
permissions.header=Изменить разрешения
|
permissions.header=Изменить разрешения
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=Öppnas i samma fönster
|
|||||||
settings.downloadOption.2=Öppna i nytt fönster
|
settings.downloadOption.2=Öppna i nytt fönster
|
||||||
settings.downloadOption.3=Ladda ner fil
|
settings.downloadOption.3=Ladda ner fil
|
||||||
settings.zipThreshold=Zip-filer när antalet nedladdade filer överskrider
|
settings.zipThreshold=Zip-filer när antalet nedladdade filer överskrider
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=Lägg till bild
|
|||||||
#merge
|
#merge
|
||||||
merge.title=Sammanfoga
|
merge.title=Sammanfoga
|
||||||
merge.header=Slå samman flera PDF-filer (2+)
|
merge.header=Slå samman flera PDF-filer (2+)
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=Slå samman
|
merge.submit=Slå samman
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=Rotation (0-360):
|
|||||||
watermark.selectText.5=widthSpacer (mellanrum mellan varje vattenstämpel horisontellt):
|
watermark.selectText.5=widthSpacer (mellanrum mellan varje vattenstämpel horisontellt):
|
||||||
watermark.selectText.6=heightSpacer (mellanrum mellan varje vattenstämpel vertikalt):
|
watermark.selectText.6=heightSpacer (mellanrum mellan varje vattenstämpel vertikalt):
|
||||||
watermark.selectText.7=Opacitet (0% - 100%):
|
watermark.selectText.7=Opacitet (0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Lägg till vattenstämpel
|
watermark.submit=Lägg till vattenstämpel
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=Ta bort vattenstämpel
|
|
||||||
remove-watermark.header=Ta bort vattenstämpel
|
|
||||||
remove-watermark.selectText.1=Välj PDF för att ta bort vattenstämpel från:
|
|
||||||
remove-watermark.selectText.2=Vattenstämpeltext:
|
|
||||||
remove-watermark.submit=Ta bort vattenstämpel
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Ändra behörigheter
|
permissions.title=Ändra behörigheter
|
||||||
permissions.header=Ändra behörigheter
|
permissions.header=Ändra behörigheter
|
||||||
|
@ -31,7 +31,14 @@ sizes.medium=Medium
|
|||||||
sizes.large=Large
|
sizes.large=Large
|
||||||
sizes.x-large=X-Large
|
sizes.x-large=X-Large
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
delete=Delete
|
||||||
|
username=Username
|
||||||
|
password=Password
|
||||||
|
welcome=Welcome
|
||||||
|
=Property
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -54,6 +61,50 @@ settings.downloadOption.1=在同一窗口打开
|
|||||||
settings.downloadOption.2=在新窗口中打开
|
settings.downloadOption.2=在新窗口中打开
|
||||||
settings.downloadOption.3=下载文件
|
settings.downloadOption.3=下载文件
|
||||||
settings.zipThreshold=当下载的文件数量超过限制时,将文件压缩。
|
settings.zipThreshold=当下载的文件数量超过限制时,将文件压缩。
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
settings.signOut=Sign Out
|
||||||
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
account.title=Account Settings
|
||||||
|
account.accountSettings=Account Settings
|
||||||
|
account.adminSettings=Admin Settings - View and Add Users
|
||||||
|
account.userControlSettings=User Control Settings
|
||||||
|
account.changeUsername=New Username
|
||||||
|
account.changeUsername=Change Username
|
||||||
|
account.password=Confirmation Password
|
||||||
|
account.oldPassword=Old password
|
||||||
|
account.newPassword=New Password
|
||||||
|
account.changePassword=Change Password
|
||||||
|
account.confirmNewPassword=Confirm New Password
|
||||||
|
account.signOut=Sign Out
|
||||||
|
account.yourApiKey=Your API Key
|
||||||
|
account.syncTitle=Sync browser settings with Account
|
||||||
|
account.settingsCompare=Settings Comparison:
|
||||||
|
account.property=Property
|
||||||
|
account.webBrowserSettings=Web Browser Setting
|
||||||
|
account.syncToBrowser=Sync Account -> Browser
|
||||||
|
account.syncToAccount=Sync Account <- Browser
|
||||||
|
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
adminUserSettings.title=User Control Settings
|
||||||
|
adminUserSettings.header=Admin User Control Settings
|
||||||
|
adminUserSettings.admin=Admin
|
||||||
|
adminUserSettings.user=User
|
||||||
|
adminUserSettings.addUser=Add New User
|
||||||
|
adminUserSettings.roles=Roles
|
||||||
|
adminUserSettings.role=Role
|
||||||
|
adminUserSettings.actions=Actions
|
||||||
|
adminUserSettings.apiUser=Limited API User
|
||||||
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -256,9 +307,6 @@ home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
|
|||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=single page
|
||||||
|
|
||||||
|
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
home.showJS.title=Show Javascript
|
home.showJS.title=Show Javascript
|
||||||
home.showJS.desc=Searches and displays any JS injected into a PDF
|
home.showJS.desc=Searches and displays any JS injected into a PDF
|
||||||
showJS.tags=JS
|
showJS.tags=JS
|
||||||
@ -269,9 +317,6 @@ showJS.tags=JS
|
|||||||
# #
|
# #
|
||||||
###########################
|
###########################
|
||||||
#showJS
|
#showJS
|
||||||
##########################
|
|
||||||
### TODO: Translate ###
|
|
||||||
##########################
|
|
||||||
showJS.title=Show Javascript
|
showJS.title=Show Javascript
|
||||||
showJS.header=Show Javascript
|
showJS.header=Show Javascript
|
||||||
showJS.downloadJS=Download Javascript
|
showJS.downloadJS=Download Javascript
|
||||||
@ -526,6 +571,11 @@ addImage.submit=添加图片
|
|||||||
#merge
|
#merge
|
||||||
merge.title=合并
|
merge.title=合并
|
||||||
merge.header=合并多个PDF(2个以上)。
|
merge.header=合并多个PDF(2个以上)。
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
merge.sortByName=Sort by name
|
||||||
|
merge.sortByDate=Sort by date
|
||||||
merge.submit=合并
|
merge.submit=合并
|
||||||
|
|
||||||
|
|
||||||
@ -626,17 +676,14 @@ watermark.selectText.4=旋转(0-360):
|
|||||||
watermark.selectText.5=widthSpacer(水平方向上每个水印之间的空间):
|
watermark.selectText.5=widthSpacer(水平方向上每个水印之间的空间):
|
||||||
watermark.selectText.6=heightSpacer(每个水印之间的垂直空间):
|
watermark.selectText.6=heightSpacer(每个水印之间的垂直空间):
|
||||||
watermark.selectText.7=透明度(0% - 100%):
|
watermark.selectText.7=透明度(0% - 100%):
|
||||||
|
##########################
|
||||||
|
### TODO: Translate ###
|
||||||
|
##########################
|
||||||
|
watermark.selectText.8=Watermark Type:
|
||||||
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=添加水印
|
watermark.submit=添加水印
|
||||||
|
|
||||||
|
|
||||||
#remove-watermark
|
|
||||||
remove-watermark.title=去除水印
|
|
||||||
remove-watermark.header=去除水印
|
|
||||||
remove-watermark.selectText.1=选择要去除水印的PDF:
|
|
||||||
remove-watermark.selectText.2=水印文本:
|
|
||||||
remove-watermark.submit=移除水印
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=更改权限
|
permissions.title=更改权限
|
||||||
permissions.header=改变权限
|
permissions.header=改变权限
|
||||||
|
4
src/main/resources/static/images/clipboard.svg
Normal file
4
src/main/resources/static/images/clipboard.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
|
||||||
|
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
|
||||||
|
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 496 B |
5
src/main/resources/static/images/eye-slash.svg
Normal file
5
src/main/resources/static/images/eye-slash.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye-slash" viewBox="0 0 16 16">
|
||||||
|
<path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z"/>
|
||||||
|
<path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z"/>
|
||||||
|
<path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 891 B |
4
src/main/resources/static/images/eye.svg
Normal file
4
src/main/resources/static/images/eye.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
|
||||||
|
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
||||||
|
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 569 B |
@ -1,63 +1,113 @@
|
|||||||
|
let currentSort = {
|
||||||
|
field: null,
|
||||||
|
descending: false
|
||||||
|
};
|
||||||
|
|
||||||
document.getElementById("fileInput-input").addEventListener("change", function() {
|
document.getElementById("fileInput-input").addEventListener("change", function() {
|
||||||
var files = this.files;
|
var files = this.files;
|
||||||
var list = document.getElementById("selectedFiles");
|
displayFiles(files);
|
||||||
list.innerHTML = "";
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
|
||||||
var item = document.createElement("li");
|
|
||||||
item.className = "list-group-item";
|
|
||||||
item.innerHTML = `
|
|
||||||
<div class="d-flex justify-content-between align-items-center w-100">
|
|
||||||
<div class="filename">${files[i].name}</div>
|
|
||||||
<div class="arrows d-flex">
|
|
||||||
<button class="btn btn-secondary move-up"><span>↑</span></button>
|
|
||||||
<button class="btn btn-secondary move-down"><span>↓</span></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
list.appendChild(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
var moveUpButtons = document.querySelectorAll(".move-up");
|
|
||||||
for (var i = 0; i < moveUpButtons.length; i++) {
|
|
||||||
moveUpButtons[i].addEventListener("click", function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var parent = this.closest(".list-group-item");
|
|
||||||
var grandParent = parent.parentNode;
|
|
||||||
if (parent.previousElementSibling) {
|
|
||||||
grandParent.insertBefore(parent, parent.previousElementSibling);
|
|
||||||
updateFiles();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var moveDownButtons = document.querySelectorAll(".move-down");
|
|
||||||
for (var i = 0; i < moveDownButtons.length; i++) {
|
|
||||||
moveDownButtons[i].addEventListener("click", function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var parent = this.closest(".list-group-item");
|
|
||||||
var grandParent = parent.parentNode;
|
|
||||||
if (parent.nextElementSibling) {
|
|
||||||
grandParent.insertBefore(parent.nextElementSibling, parent);
|
|
||||||
updateFiles();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFiles() {
|
|
||||||
var dataTransfer = new DataTransfer();
|
|
||||||
var liElements = document.querySelectorAll("#selectedFiles li");
|
|
||||||
|
|
||||||
for (var i = 0; i < liElements.length; i++) {
|
|
||||||
var fileNameFromList = liElements[i].querySelector(".filename").innerText;
|
|
||||||
var fileFromFiles;
|
|
||||||
for (var j = 0; j < files.length; j++) {
|
|
||||||
var file = files[j];
|
|
||||||
if (file.name === fileNameFromList) {
|
|
||||||
dataTransfer.items.add(file);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.getElementById("fileInput-input").files = dataTransfer.files;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function displayFiles(files) {
|
||||||
|
var list = document.getElementById("selectedFiles");
|
||||||
|
list.innerHTML = "";
|
||||||
|
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
var item = document.createElement("li");
|
||||||
|
item.className = "list-group-item";
|
||||||
|
item.innerHTML = `
|
||||||
|
<div class="d-flex justify-content-between align-items-center w-100">
|
||||||
|
<div class="filename">${files[i].name}</div>
|
||||||
|
<div class="arrows d-flex">
|
||||||
|
<button class="btn btn-secondary move-up"><span>↑</span></button>
|
||||||
|
<button class="btn btn-secondary move-down"><span>↓</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
list.appendChild(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
attachMoveButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachMoveButtons() {
|
||||||
|
var moveUpButtons = document.querySelectorAll(".move-up");
|
||||||
|
for (var i = 0; i < moveUpButtons.length; i++) {
|
||||||
|
moveUpButtons[i].addEventListener("click", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var parent = this.closest(".list-group-item");
|
||||||
|
var grandParent = parent.parentNode;
|
||||||
|
if (parent.previousElementSibling) {
|
||||||
|
grandParent.insertBefore(parent, parent.previousElementSibling);
|
||||||
|
updateFiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var moveDownButtons = document.querySelectorAll(".move-down");
|
||||||
|
for (var i = 0; i < moveDownButtons.length; i++) {
|
||||||
|
moveDownButtons[i].addEventListener("click", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var parent = this.closest(".list-group-item");
|
||||||
|
var grandParent = parent.parentNode;
|
||||||
|
if (parent.nextElementSibling) {
|
||||||
|
grandParent.insertBefore(parent.nextElementSibling, parent);
|
||||||
|
updateFiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("sortByNameBtn").addEventListener("click", function() {
|
||||||
|
if (currentSort.field === "name" && !currentSort.descending) {
|
||||||
|
currentSort.descending = true;
|
||||||
|
sortFiles((a, b) => b.name.localeCompare(a.name));
|
||||||
|
} else {
|
||||||
|
currentSort.field = "name";
|
||||||
|
currentSort.descending = false;
|
||||||
|
sortFiles((a, b) => a.name.localeCompare(b.name));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("sortByDateBtn").addEventListener("click", function() {
|
||||||
|
if (currentSort.field === "lastModified" && !currentSort.descending) {
|
||||||
|
currentSort.descending = true;
|
||||||
|
sortFiles((a, b) => b.lastModified - a.lastModified);
|
||||||
|
} else {
|
||||||
|
currentSort.field = "lastModified";
|
||||||
|
currentSort.descending = false;
|
||||||
|
sortFiles((a, b) => a.lastModified - b.lastModified);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function sortFiles(comparator) {
|
||||||
|
// Convert FileList to array and sort
|
||||||
|
const sortedFilesArray = Array.from(document.getElementById("fileInput-input").files).sort(comparator);
|
||||||
|
|
||||||
|
// Refresh displayed list
|
||||||
|
displayFiles(sortedFilesArray);
|
||||||
|
|
||||||
|
// Update the files property
|
||||||
|
const dataTransfer = new DataTransfer();
|
||||||
|
sortedFilesArray.forEach(file => dataTransfer.items.add(file));
|
||||||
|
document.getElementById("fileInput-input").files = dataTransfer.files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFiles() {
|
||||||
|
var dataTransfer = new DataTransfer();
|
||||||
|
var liElements = document.querySelectorAll("#selectedFiles li");
|
||||||
|
const files = document.getElementById("fileInput-input").files;
|
||||||
|
|
||||||
|
for (var i = 0; i < liElements.length; i++) {
|
||||||
|
var fileNameFromList = liElements[i].querySelector(".filename").innerText;
|
||||||
|
var fileFromFiles;
|
||||||
|
for (var j = 0; j < files.length; j++) {
|
||||||
|
var file = files[j];
|
||||||
|
if (file.name === fileNameFromList) {
|
||||||
|
dataTransfer.items.add(file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById("fileInput-input").files = dataTransfer.files;
|
||||||
|
}
|
||||||
|
295
src/main/resources/templates/account.html
Normal file
295
src/main/resources/templates/account.html
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<th:block th:insert="~{fragments/common :: head(title=#{account.title})}"></th:block>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-9">
|
||||||
|
|
||||||
|
<!-- User Settings Title -->
|
||||||
|
<h2 class="text-center" th:text="#{account.accountSettings}">User Settings</h2>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- At the top of the user settings -->
|
||||||
|
<h3 class="text-center"><span th:text="#{welcome} + ' ' + ${username}">User</span>!</h3>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Change Username Form -->
|
||||||
|
<h4></h4>
|
||||||
|
<form action="/change-username" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="newUsername" th:text="#{account.changeUsername}">Change Username</label>
|
||||||
|
<input type="text" class="form-control" name="newUsername" id="newUsername" placeholder="New Username">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="currentPassword" th:text="#{password}">Password</label>
|
||||||
|
<input type="password" class="form-control" name="currentPassword" id="currentPassword" placeholder="Password">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-primary" th:text="#{account.changeUsername}">Change Username</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr> <!-- Separator Line -->
|
||||||
|
|
||||||
|
<!-- Change Password Form -->
|
||||||
|
<h4 th:text="#{account.changePassword}">Change Password?</h4>
|
||||||
|
<form action="/change-password" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="currentPassword" th:text="#{account.oldPassword}">Old Password</label>
|
||||||
|
<input type="password" class="form-control" name="currentPassword" id="currentPassword" th:placeholder="#{account.oldPassword}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="newPassword" th:text="#{account.newPassword}">New Password</label>
|
||||||
|
<input type="password" class="form-control" name="newPassword" id="newPassword" th:placeholder="#{account.newPassword}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="confirmNewPassword" th:text="#{account.confirmNewPassword}">Confirm New Password</label>
|
||||||
|
<input type="password" class="form-control" name="confirmNewPassword" id="confirmNewPassword" th:placeholder="#{account.confirmNewPassword}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-primary" th:text="#{account.changePassword}">Change Password</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header" th:text="#{account.yourApiKey}">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="password" class="form-control" id="apiKey" th:placeholder="#{account.yourApiKey}" readonly>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-secondary" id="copyBtn" type="button" onclick="copyToClipboard()">
|
||||||
|
<img src="images/clipboard.svg" alt="Copy" style="height:20px;">
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" id="showBtn" type="button" onclick="showApiKey()">
|
||||||
|
<img id="eyeIcon" src="images/eye.svg" alt="Toggle API Key Visibility" style="height:20px;">
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" id="refreshBtn" type="button" onclick="refreshApiKey()">
|
||||||
|
<img id="eyeIcon" src="images/arrow-clockwise.svg" alt="Refresh API-Key" style="height:20px;">
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copyToClipboard() {
|
||||||
|
const apiKeyElement = document.getElementById("apiKey");
|
||||||
|
apiKeyElement.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function showApiKey() {
|
||||||
|
const apiKeyElement = document.getElementById("apiKey");
|
||||||
|
const copyBtn = document.getElementById("copyBtn");
|
||||||
|
const eyeIcon = document.getElementById("eyeIcon");
|
||||||
|
if (apiKeyElement.type === "password") {
|
||||||
|
apiKeyElement.type = "text";
|
||||||
|
eyeIcon.src = "images/eye-slash.svg";
|
||||||
|
copyBtn.disabled = false; // Enable copy button when API key is visible
|
||||||
|
} else {
|
||||||
|
apiKeyElement.type = "password";
|
||||||
|
eyeIcon.src = "images/eye.svg";
|
||||||
|
copyBtn.disabled = true; // Disable copy button when API key is hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", async function() {
|
||||||
|
try {
|
||||||
|
let response = await fetch('/get-api-key', { method: 'POST' });
|
||||||
|
if (response.status === 200) {
|
||||||
|
let apiKey = await response.text();
|
||||||
|
manageUIState(apiKey);
|
||||||
|
} else {
|
||||||
|
manageUIState(null);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('There was an error:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function refreshApiKey() {
|
||||||
|
try {
|
||||||
|
let response = await fetch('/update-api-key', { method: 'POST' });
|
||||||
|
if (response.status === 200) {
|
||||||
|
let apiKey = await response.text();
|
||||||
|
manageUIState(apiKey);
|
||||||
|
document.getElementById("apiKey").type = 'text';
|
||||||
|
document.getElementById("copyBtn").disabled = false;
|
||||||
|
} else {
|
||||||
|
alert('Error refreshing API key.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('There was an error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function manageUIState(apiKey) {
|
||||||
|
const apiKeyElement = document.getElementById("apiKey");
|
||||||
|
const showBtn = document.getElementById("showBtn");
|
||||||
|
const copyBtn = document.getElementById("copyBtn");
|
||||||
|
|
||||||
|
if (apiKey && apiKey.trim().length > 0) {
|
||||||
|
apiKeyElement.value = apiKey;
|
||||||
|
showBtn.disabled = false;
|
||||||
|
copyBtn.disabled = true;
|
||||||
|
} else {
|
||||||
|
apiKeyElement.value = "";
|
||||||
|
showBtn.disabled = true;
|
||||||
|
copyBtn.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<hr> <!-- Separator Line -->
|
||||||
|
|
||||||
|
<h4 th:text="#{account.syncTitle}">Sync browser settings with Account</h4>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h3 th:text="#{account.settingsCompare}">Settings Comparison:</h3>
|
||||||
|
<table id="settingsTable" class="table table-bordered table-sm table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th th:text="#{account.property}">Property</th>
|
||||||
|
<th th:text="#{account.accountSettings}">Account Setting</th>
|
||||||
|
<th th:text="#{account.webBrowserSettings}">Web Browser Setting</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- This will be dynamically populated by JavaScript -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="buttons-container mt-3 text-center">
|
||||||
|
<button id="syncToBrowser" class="btn btn-primary btn-sm" th:text="#{account.syncToBrowser}">Sync Account -> Browser</button>
|
||||||
|
<button id="syncToAccount" class="btn btn-secondary btn-sm" th:text="#{account.syncToAccount}">Sync Account <- Browser</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.buttons-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script th:inline="javascript">
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const settingsTableBody = document.querySelector("#settingsTable tbody");
|
||||||
|
|
||||||
|
/*<![CDATA[*/
|
||||||
|
var accountSettingsString = /*[[${settings}]]*/ {};
|
||||||
|
/*]]>*/
|
||||||
|
var accountSettings = JSON.parse(accountSettingsString);
|
||||||
|
|
||||||
|
let allKeys = new Set([...Object.keys(accountSettings), ...Object.keys(localStorage)]);
|
||||||
|
|
||||||
|
allKeys.forEach(key => {
|
||||||
|
if(key === 'debug' || key === '0' || key === '1') return; // Ignoring specific keys
|
||||||
|
|
||||||
|
const accountValue = accountSettings[key] || '-';
|
||||||
|
const browserValue = localStorage.getItem(key) || '-';
|
||||||
|
|
||||||
|
const row = settingsTableBody.insertRow();
|
||||||
|
const propertyCell = row.insertCell(0);
|
||||||
|
const accountCell = row.insertCell(1);
|
||||||
|
const browserCell = row.insertCell(2);
|
||||||
|
|
||||||
|
propertyCell.textContent = key;
|
||||||
|
accountCell.textContent = accountValue;
|
||||||
|
browserCell.textContent = browserValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('syncToBrowser').addEventListener('click', function() {
|
||||||
|
// First, clear the local storage
|
||||||
|
localStorage.clear();
|
||||||
|
|
||||||
|
// Then, set the account settings to local storage
|
||||||
|
for (let key in accountSettings) {
|
||||||
|
if(key !== 'debug' && key !== '0' && key !== '1') { // Only sync non-ignored keys
|
||||||
|
localStorage.setItem(key, accountSettings[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location.reload(); // Refresh the page after sync
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById('syncToAccount').addEventListener('click', function() {
|
||||||
|
let form = document.createElement("form");
|
||||||
|
form.method = "POST";
|
||||||
|
form.action = "/updateUserSettings"; // Your endpoint URL
|
||||||
|
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if(key !== 'debug' && key !== '0' && key !== '1') { // Only send non-ignored keys
|
||||||
|
let hiddenField = document.createElement("input");
|
||||||
|
hiddenField.type = "hidden";
|
||||||
|
hiddenField.name = key;
|
||||||
|
hiddenField.value = localStorage.getItem(key);
|
||||||
|
form.appendChild(hiddenField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.appendChild(form);
|
||||||
|
form.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group mt-4">
|
||||||
|
<a href="/logout">
|
||||||
|
<button type="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</button>
|
||||||
|
</a>
|
||||||
|
<a th:if="${role == 'ROLE_ADMIN'}" href="addUsers" target="_blank">
|
||||||
|
<button type="button" class="btn btn-info" th:text="#{account.adminSettings}">Admin Settings</button>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
73
src/main/resources/templates/addUsers.html
Normal file
73
src/main/resources/templates/addUsers.html
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<th:block th:insert="~{fragments/common :: head(title=#{adminUserSettings.title})}"></th:block>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
|
||||||
|
<!-- User Settings Title -->
|
||||||
|
<h2 class="text-center" th:text="#{adminUserSettings.header}">Admin User Control Settings</h2>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th th:text="#{username}">Username</th>
|
||||||
|
<th th:text="#{adminUserSettings.roles}">Roles</th>
|
||||||
|
<th th:text="#{adminUserSettings.actions}">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="user : ${users}">
|
||||||
|
<td th:text="${user.username}"></td>
|
||||||
|
<td th:text="${user.getRolesAsString()}"></td>
|
||||||
|
<td>
|
||||||
|
<a th:href="@{'/admin/deleteUser/' + ${user.username}}" th:text="#{delete}">Delete</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<h2 th:text="#{adminUserSettings.addUser}">Add New User</h2>
|
||||||
|
<form action="/admin/saveUser" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username" th:text="#{username}">Username</label>
|
||||||
|
<input type="text" class="form-control" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" th:text="#{password}">Password</label>
|
||||||
|
<input type="password" class="form-control" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
||||||
|
<select name="role" class="form-control" required>
|
||||||
|
<option value="ROLE_ADMIN" th:text="#{adminUserSettings.admin}">Admin</option>
|
||||||
|
<option value="ROLE_USER" th:text="#{adminUserSettings.user}">User</option>
|
||||||
|
<option value="ROLE_LIMITED_API_USER" th:text="#{adminUserSettings.apiUser}">Limited API User</option>
|
||||||
|
<option value="ROLE_WEB_ONLY_USER" th:text="#{adminUserSettings.webOnlyUser}">Web Only User</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add other fields as required -->
|
||||||
|
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -341,9 +341,18 @@
|
|||||||
<label class="custom-control-label" for="boredWaiting" th:text="#{bored}"></label>
|
<label class="custom-control-label" for="boredWaiting" th:text="#{bored}"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a th:if="${@loginEnabled}" href="account" target="_blank">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary" th:text="#{settings.accountSettings}">Account Settings</button>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
<a th:if="${@loginEnabled}" href="/logout">
|
||||||
|
<button type="button" class="btn btn-danger" th:text="#{settings.signOut}">Sign Out</button>
|
||||||
|
</a>
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
59
src/main/resources/templates/login.html
Normal file
59
src/main/resources/templates/login.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<!-- Hi if you have been redirected here when using API then you might need to supply a X-API-KEY key in header to authenticate! -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<title>Login</title>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center align-items-center" style="height:100vh;">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="login-container card">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<img src="favicon.svg" alt="Logo" class="img-fluid" style="max-width: 100px;"> <!-- Adjust path and style as needed -->
|
||||||
|
<h4>Stirling-PDF Login</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div th:if="${logoutMessage}" class="alert alert-success" th:text="${logoutMessage}"></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<form th:action="@{/login}" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input type="text" id="username" name="username" class="form-control" required="required" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input type="password" id="password" name="password" class="form-control" required="required" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group text-center">
|
||||||
|
<input type="submit" value="Login" class="btn btn-primary" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-danger text-center">
|
||||||
|
<div th:if="${error == 'badcredentials'}">
|
||||||
|
Invalid username or password.
|
||||||
|
</div>
|
||||||
|
<div th:if="${error == 'locked'}">
|
||||||
|
Your account has been locked.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -23,6 +23,8 @@
|
|||||||
<ul id="selectedFiles" class="list-group"></ul>
|
<ul id="selectedFiles" class="list-group"></ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group text-center">
|
<div class="form-group text-center">
|
||||||
|
<button type="button" id="sortByNameBtn" class="btn btn-info" th:text="#{merge.sortByName}"></button>
|
||||||
|
<button type="button" id="sortByDateBtn" class="btn btn-info" th:text="#{merge.sortByDate}"></button>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{merge.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{merge.submit}"></button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user