Make ./gradlew executable; accept .java files improvements after running ./gradlew build
This commit is contained in:
parent
de9e9a0f84
commit
8a57165547
29 changed files with 3006 additions and 3007 deletions
0
gradlew
vendored
Normal file → Executable file
0
gradlew
vendored
Normal file → Executable file
|
@ -1,81 +1,81 @@
|
||||||
package stirling.software.SPDF;
|
package stirling.software.SPDF;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.SpringApplication;
|
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 jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import stirling.software.SPDF.config.ConfigInitializer;
|
import stirling.software.SPDF.config.ConfigInitializer;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
|
||||||
@Autowired private Environment env;
|
@Autowired private Environment env;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// Check if the BROWSER_OPEN environment variable is set to true
|
// Check if the BROWSER_OPEN environment variable is set to true
|
||||||
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
||||||
boolean browserOpen = browserOpenEnv != null && browserOpenEnv.equalsIgnoreCase("true");
|
boolean browserOpen = browserOpenEnv != null && browserOpenEnv.equalsIgnoreCase("true");
|
||||||
|
|
||||||
if (browserOpen) {
|
if (browserOpen) {
|
||||||
try {
|
try {
|
||||||
String url = "http://localhost:" + getPort();
|
String url = "http://localhost:" + getPort();
|
||||||
|
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
Runtime rt = Runtime.getRuntime();
|
Runtime rt = Runtime.getRuntime();
|
||||||
if (os.contains("win")) {
|
if (os.contains("win")) {
|
||||||
// For Windows
|
// For Windows
|
||||||
rt.exec("rundll32 url.dll,FileProtocolHandler " + url);
|
rt.exec("rundll32 url.dll,FileProtocolHandler " + url);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||||
app.addInitializers(new ConfigInitializer());
|
app.addInitializers(new ConfigInitializer());
|
||||||
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
||||||
app.setDefaultProperties(
|
app.setDefaultProperties(
|
||||||
Collections.singletonMap(
|
Collections.singletonMap(
|
||||||
"spring.config.additional-location", "file:configs/settings.yml"));
|
"spring.config.additional-location", "file:configs/settings.yml"));
|
||||||
} else {
|
} else {
|
||||||
System.out.println(
|
System.out.println(
|
||||||
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
||||||
}
|
}
|
||||||
app.run(args);
|
app.run(args);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
GeneralUtils.createDir("customFiles/static/");
|
GeneralUtils.createDir("customFiles/static/");
|
||||||
GeneralUtils.createDir("customFiles/templates/");
|
GeneralUtils.createDir("customFiles/templates/");
|
||||||
|
|
||||||
System.out.println("Stirling-PDF Started.");
|
System.out.println("Stirling-PDF Started.");
|
||||||
|
|
||||||
String url = "http://localhost:" + getPort();
|
String url = "http://localhost:" + getPort();
|
||||||
System.out.println("Navigate to " + url);
|
System.out.println("Navigate to " + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getPort() {
|
public static String getPort() {
|
||||||
String port = System.getProperty("local.server.port");
|
String port = System.getProperty("local.server.port");
|
||||||
if (port == null || port.isEmpty()) {
|
if (port == null || port.isEmpty()) {
|
||||||
port = "8080";
|
port = "8080";
|
||||||
}
|
}
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +1,60 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class AppConfig {
|
public class AppConfig {
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Bean(name = "loginEnabled")
|
@Bean(name = "loginEnabled")
|
||||||
public boolean loginEnabled() {
|
public boolean loginEnabled() {
|
||||||
return applicationProperties.getSecurity().getEnableLogin();
|
return applicationProperties.getSecurity().getEnableLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "appName")
|
@Bean(name = "appName")
|
||||||
public String appName() {
|
public String appName() {
|
||||||
String homeTitle = applicationProperties.getUi().getAppName();
|
String homeTitle = applicationProperties.getUi().getAppName();
|
||||||
return (homeTitle != null) ? homeTitle : "Stirling PDF";
|
return (homeTitle != null) ? homeTitle : "Stirling PDF";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "appVersion")
|
@Bean(name = "appVersion")
|
||||||
public String appVersion() {
|
public String appVersion() {
|
||||||
String version = getClass().getPackage().getImplementationVersion();
|
String version = getClass().getPackage().getImplementationVersion();
|
||||||
return (version != null) ? version : "0.0.0";
|
return (version != null) ? version : "0.0.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "homeText")
|
@Bean(name = "homeText")
|
||||||
public String homeText() {
|
public String homeText() {
|
||||||
return (applicationProperties.getUi().getHomeDescription() != null)
|
return (applicationProperties.getUi().getHomeDescription() != null)
|
||||||
? applicationProperties.getUi().getHomeDescription()
|
? applicationProperties.getUi().getHomeDescription()
|
||||||
: "null";
|
: "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "navBarText")
|
@Bean(name = "navBarText")
|
||||||
public String navBarText() {
|
public String navBarText() {
|
||||||
String defaultNavBar =
|
String defaultNavBar =
|
||||||
applicationProperties.getUi().getAppNameNavbar() != null
|
applicationProperties.getUi().getAppNameNavbar() != null
|
||||||
? applicationProperties.getUi().getAppNameNavbar()
|
? applicationProperties.getUi().getAppNameNavbar()
|
||||||
: applicationProperties.getUi().getAppName();
|
: applicationProperties.getUi().getAppName();
|
||||||
return (defaultNavBar != null) ? defaultNavBar : "Stirling PDF";
|
return (defaultNavBar != null) ? defaultNavBar : "Stirling PDF";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "enableAlphaFunctionality")
|
@Bean(name = "enableAlphaFunctionality")
|
||||||
public boolean enableAlphaFunctionality() {
|
public boolean enableAlphaFunctionality() {
|
||||||
return applicationProperties.getSystem().getEnableAlphaFunctionality() != null
|
return applicationProperties.getSystem().getEnableAlphaFunctionality() != null
|
||||||
? applicationProperties.getSystem().getEnableAlphaFunctionality()
|
? applicationProperties.getSystem().getEnableAlphaFunctionality()
|
||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "rateLimit")
|
@Bean(name = "rateLimit")
|
||||||
public boolean rateLimit() {
|
public boolean rateLimit() {
|
||||||
String appName = System.getProperty("rateLimit");
|
String appName = System.getProperty("rateLimit");
|
||||||
if (appName == null) appName = System.getenv("rateLimit");
|
if (appName == null) appName = System.getenv("rateLimit");
|
||||||
return (appName != null) ? Boolean.valueOf(appName) : false;
|
return (appName != null) ? Boolean.valueOf(appName) : false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,64 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
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 stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class Beans implements WebMvcConfigurer {
|
public class Beans implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(localeChangeInterceptor());
|
registry.addInterceptor(localeChangeInterceptor());
|
||||||
registry.addInterceptor(new CleanUrlInterceptor());
|
registry.addInterceptor(new CleanUrlInterceptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LocaleChangeInterceptor localeChangeInterceptor() {
|
public LocaleChangeInterceptor localeChangeInterceptor() {
|
||||||
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
|
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
|
||||||
lci.setParamName("lang");
|
lci.setParamName("lang");
|
||||||
return lci;
|
return lci;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LocaleResolver localeResolver() {
|
public LocaleResolver localeResolver() {
|
||||||
SessionLocaleResolver slr = new SessionLocaleResolver();
|
SessionLocaleResolver slr = new SessionLocaleResolver();
|
||||||
|
|
||||||
String appLocaleEnv = applicationProperties.getSystem().getDefaultLocale();
|
String appLocaleEnv = applicationProperties.getSystem().getDefaultLocale();
|
||||||
Locale defaultLocale =
|
Locale defaultLocale =
|
||||||
Locale.UK; // Fallback to UK locale if environment variable is not set
|
Locale.UK; // Fallback to UK locale if environment variable is not set
|
||||||
|
|
||||||
if (appLocaleEnv != null && !appLocaleEnv.isEmpty()) {
|
if (appLocaleEnv != null && !appLocaleEnv.isEmpty()) {
|
||||||
Locale tempLocale = Locale.forLanguageTag(appLocaleEnv);
|
Locale tempLocale = Locale.forLanguageTag(appLocaleEnv);
|
||||||
String tempLanguageTag = tempLocale.toLanguageTag();
|
String tempLanguageTag = tempLocale.toLanguageTag();
|
||||||
|
|
||||||
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
||||||
defaultLocale = tempLocale;
|
defaultLocale = tempLocale;
|
||||||
} else {
|
} else {
|
||||||
tempLocale = Locale.forLanguageTag(appLocaleEnv.replace("_", "-"));
|
tempLocale = Locale.forLanguageTag(appLocaleEnv.replace("_", "-"));
|
||||||
tempLanguageTag = tempLocale.toLanguageTag();
|
tempLanguageTag = tempLocale.toLanguageTag();
|
||||||
|
|
||||||
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
||||||
defaultLocale = tempLocale;
|
defaultLocale = tempLocale;
|
||||||
} else {
|
} else {
|
||||||
System.err.println(
|
System.err.println(
|
||||||
"Invalid APP_LOCALE environment variable value. Falling back to default Locale.UK.");
|
"Invalid APP_LOCALE environment variable value. Falling back to default Locale.UK.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slr.setDefaultLocale(defaultLocale);
|
slr.setDefaultLocale(defaultLocale);
|
||||||
return slr;
|
return slr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +1,74 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
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 org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
public class CleanUrlInterceptor implements HandlerInterceptor {
|
public class CleanUrlInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
private static final List<String> ALLOWED_PARAMS =
|
private static final List<String> ALLOWED_PARAMS =
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
"lang", "endpoint", "endpoints", "logout", "error", "file", "messageType");
|
"lang", "endpoint", "endpoints", "logout", "error", "file", "messageType");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(
|
public boolean preHandle(
|
||||||
HttpServletRequest request, HttpServletResponse response, Object handler)
|
HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String queryString = request.getQueryString();
|
String queryString = request.getQueryString();
|
||||||
if (queryString != null && !queryString.isEmpty()) {
|
if (queryString != null && !queryString.isEmpty()) {
|
||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
Map<String, String> parameters = new HashMap<>();
|
Map<String, String> parameters = new HashMap<>();
|
||||||
|
|
||||||
// Keep only the allowed parameters
|
// Keep only the allowed parameters
|
||||||
String[] queryParameters = queryString.split("&");
|
String[] queryParameters = queryString.split("&");
|
||||||
for (String param : queryParameters) {
|
for (String param : queryParameters) {
|
||||||
String[] keyValue = param.split("=");
|
String[] keyValue = param.split("=");
|
||||||
if (keyValue.length != 2) {
|
if (keyValue.length != 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ALLOWED_PARAMS.contains(keyValue[0])) {
|
if (ALLOWED_PARAMS.contains(keyValue[0])) {
|
||||||
parameters.put(keyValue[0], keyValue[1]);
|
parameters.put(keyValue[0], keyValue[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are any parameters that are not allowed
|
// If there are any parameters that are not allowed
|
||||||
if (parameters.size() != queryParameters.length) {
|
if (parameters.size() != queryParameters.length) {
|
||||||
// Construct new query string
|
// Construct new query string
|
||||||
StringBuilder newQueryString = new StringBuilder();
|
StringBuilder newQueryString = new StringBuilder();
|
||||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||||
if (newQueryString.length() > 0) {
|
if (newQueryString.length() > 0) {
|
||||||
newQueryString.append("&");
|
newQueryString.append("&");
|
||||||
}
|
}
|
||||||
newQueryString.append(entry.getKey()).append("=").append(entry.getValue());
|
newQueryString.append(entry.getKey()).append("=").append(entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to the URL with only allowed query parameters
|
// Redirect to the URL with only allowed query parameters
|
||||||
String redirectUrl = requestURI + "?" + newQueryString;
|
String redirectUrl = requestURI + "?" + newQueryString;
|
||||||
response.sendRedirect(redirectUrl);
|
response.sendRedirect(redirectUrl);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postHandle(
|
public void postHandle(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
Object handler,
|
Object handler,
|
||||||
ModelAndView modelAndView) {}
|
ModelAndView modelAndView) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterCompletion(
|
public void afterCompletion(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
Object handler,
|
Object handler,
|
||||||
Exception ex) {}
|
Exception ex) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,231 +1,231 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class EndpointConfiguration {
|
public class EndpointConfiguration {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
|
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
|
||||||
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
||||||
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
|
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EndpointConfiguration(ApplicationProperties applicationProperties) {
|
public EndpointConfiguration(ApplicationProperties applicationProperties) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
init();
|
init();
|
||||||
processEnvironmentConfigs();
|
processEnvironmentConfigs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableEndpoint(String endpoint) {
|
public void enableEndpoint(String endpoint) {
|
||||||
endpointStatuses.put(endpoint, true);
|
endpointStatuses.put(endpoint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableEndpoint(String endpoint) {
|
public void disableEndpoint(String endpoint) {
|
||||||
if (!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) {
|
if (!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) {
|
||||||
logger.info("Disabling {}", endpoint);
|
logger.info("Disabling {}", endpoint);
|
||||||
endpointStatuses.put(endpoint, false);
|
endpointStatuses.put(endpoint, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEndpointEnabled(String endpoint) {
|
public boolean isEndpointEnabled(String endpoint) {
|
||||||
if (endpoint.startsWith("/")) {
|
if (endpoint.startsWith("/")) {
|
||||||
endpoint = endpoint.substring(1);
|
endpoint = endpoint.substring(1);
|
||||||
}
|
}
|
||||||
return endpointStatuses.getOrDefault(endpoint, true);
|
return endpointStatuses.getOrDefault(endpoint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEndpointToGroup(String group, String endpoint) {
|
public void addEndpointToGroup(String group, String endpoint) {
|
||||||
endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint);
|
endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableGroup(String group) {
|
public void enableGroup(String group) {
|
||||||
Set<String> endpoints = endpointGroups.get(group);
|
Set<String> endpoints = endpointGroups.get(group);
|
||||||
if (endpoints != null) {
|
if (endpoints != null) {
|
||||||
for (String endpoint : endpoints) {
|
for (String endpoint : endpoints) {
|
||||||
enableEndpoint(endpoint);
|
enableEndpoint(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableGroup(String group) {
|
public void disableGroup(String group) {
|
||||||
Set<String> endpoints = endpointGroups.get(group);
|
Set<String> endpoints = endpointGroups.get(group);
|
||||||
if (endpoints != null) {
|
if (endpoints != null) {
|
||||||
for (String endpoint : endpoints) {
|
for (String endpoint : endpoints) {
|
||||||
disableEndpoint(endpoint);
|
disableEndpoint(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
// Adding endpoints to "PageOps" group
|
// Adding endpoints to "PageOps" group
|
||||||
addEndpointToGroup("PageOps", "remove-pages");
|
addEndpointToGroup("PageOps", "remove-pages");
|
||||||
addEndpointToGroup("PageOps", "merge-pdfs");
|
addEndpointToGroup("PageOps", "merge-pdfs");
|
||||||
addEndpointToGroup("PageOps", "split-pdfs");
|
addEndpointToGroup("PageOps", "split-pdfs");
|
||||||
addEndpointToGroup("PageOps", "pdf-organizer");
|
addEndpointToGroup("PageOps", "pdf-organizer");
|
||||||
addEndpointToGroup("PageOps", "rotate-pdf");
|
addEndpointToGroup("PageOps", "rotate-pdf");
|
||||||
addEndpointToGroup("PageOps", "multi-page-layout");
|
addEndpointToGroup("PageOps", "multi-page-layout");
|
||||||
addEndpointToGroup("PageOps", "scale-pages");
|
addEndpointToGroup("PageOps", "scale-pages");
|
||||||
addEndpointToGroup("PageOps", "adjust-contrast");
|
addEndpointToGroup("PageOps", "adjust-contrast");
|
||||||
addEndpointToGroup("PageOps", "crop");
|
addEndpointToGroup("PageOps", "crop");
|
||||||
addEndpointToGroup("PageOps", "auto-split-pdf");
|
addEndpointToGroup("PageOps", "auto-split-pdf");
|
||||||
addEndpointToGroup("PageOps", "extract-page");
|
addEndpointToGroup("PageOps", "extract-page");
|
||||||
addEndpointToGroup("PageOps", "pdf-to-single-page");
|
addEndpointToGroup("PageOps", "pdf-to-single-page");
|
||||||
addEndpointToGroup("PageOps", "split-by-size-or-count");
|
addEndpointToGroup("PageOps", "split-by-size-or-count");
|
||||||
addEndpointToGroup("PageOps", "overlay-pdf");
|
addEndpointToGroup("PageOps", "overlay-pdf");
|
||||||
addEndpointToGroup("PageOps", "split-pdf-by-sections");
|
addEndpointToGroup("PageOps", "split-pdf-by-sections");
|
||||||
|
|
||||||
// Adding endpoints to "Convert" group
|
// Adding endpoints to "Convert" group
|
||||||
addEndpointToGroup("Convert", "pdf-to-img");
|
addEndpointToGroup("Convert", "pdf-to-img");
|
||||||
addEndpointToGroup("Convert", "img-to-pdf");
|
addEndpointToGroup("Convert", "img-to-pdf");
|
||||||
addEndpointToGroup("Convert", "pdf-to-pdfa");
|
addEndpointToGroup("Convert", "pdf-to-pdfa");
|
||||||
addEndpointToGroup("Convert", "file-to-pdf");
|
addEndpointToGroup("Convert", "file-to-pdf");
|
||||||
addEndpointToGroup("Convert", "xlsx-to-pdf");
|
addEndpointToGroup("Convert", "xlsx-to-pdf");
|
||||||
addEndpointToGroup("Convert", "pdf-to-word");
|
addEndpointToGroup("Convert", "pdf-to-word");
|
||||||
addEndpointToGroup("Convert", "pdf-to-presentation");
|
addEndpointToGroup("Convert", "pdf-to-presentation");
|
||||||
addEndpointToGroup("Convert", "pdf-to-text");
|
addEndpointToGroup("Convert", "pdf-to-text");
|
||||||
addEndpointToGroup("Convert", "pdf-to-html");
|
addEndpointToGroup("Convert", "pdf-to-html");
|
||||||
addEndpointToGroup("Convert", "pdf-to-xml");
|
addEndpointToGroup("Convert", "pdf-to-xml");
|
||||||
addEndpointToGroup("Convert", "html-to-pdf");
|
addEndpointToGroup("Convert", "html-to-pdf");
|
||||||
addEndpointToGroup("Convert", "url-to-pdf");
|
addEndpointToGroup("Convert", "url-to-pdf");
|
||||||
addEndpointToGroup("Convert", "markdown-to-pdf");
|
addEndpointToGroup("Convert", "markdown-to-pdf");
|
||||||
addEndpointToGroup("Convert", "pdf-to-csv");
|
addEndpointToGroup("Convert", "pdf-to-csv");
|
||||||
|
|
||||||
// Adding endpoints to "Security" group
|
// Adding endpoints to "Security" group
|
||||||
addEndpointToGroup("Security", "add-password");
|
addEndpointToGroup("Security", "add-password");
|
||||||
addEndpointToGroup("Security", "remove-password");
|
addEndpointToGroup("Security", "remove-password");
|
||||||
addEndpointToGroup("Security", "change-permissions");
|
addEndpointToGroup("Security", "change-permissions");
|
||||||
addEndpointToGroup("Security", "add-watermark");
|
addEndpointToGroup("Security", "add-watermark");
|
||||||
addEndpointToGroup("Security", "cert-sign");
|
addEndpointToGroup("Security", "cert-sign");
|
||||||
addEndpointToGroup("Security", "sanitize-pdf");
|
addEndpointToGroup("Security", "sanitize-pdf");
|
||||||
addEndpointToGroup("Security", "auto-redact");
|
addEndpointToGroup("Security", "auto-redact");
|
||||||
|
|
||||||
// Adding endpoints to "Other" group
|
// Adding endpoints to "Other" group
|
||||||
addEndpointToGroup("Other", "ocr-pdf");
|
addEndpointToGroup("Other", "ocr-pdf");
|
||||||
addEndpointToGroup("Other", "add-image");
|
addEndpointToGroup("Other", "add-image");
|
||||||
addEndpointToGroup("Other", "compress-pdf");
|
addEndpointToGroup("Other", "compress-pdf");
|
||||||
addEndpointToGroup("Other", "extract-images");
|
addEndpointToGroup("Other", "extract-images");
|
||||||
addEndpointToGroup("Other", "change-metadata");
|
addEndpointToGroup("Other", "change-metadata");
|
||||||
addEndpointToGroup("Other", "extract-image-scans");
|
addEndpointToGroup("Other", "extract-image-scans");
|
||||||
addEndpointToGroup("Other", "sign");
|
addEndpointToGroup("Other", "sign");
|
||||||
addEndpointToGroup("Other", "flatten");
|
addEndpointToGroup("Other", "flatten");
|
||||||
addEndpointToGroup("Other", "repair");
|
addEndpointToGroup("Other", "repair");
|
||||||
addEndpointToGroup("Other", "remove-blanks");
|
addEndpointToGroup("Other", "remove-blanks");
|
||||||
addEndpointToGroup("Other", "remove-annotations");
|
addEndpointToGroup("Other", "remove-annotations");
|
||||||
addEndpointToGroup("Other", "compare");
|
addEndpointToGroup("Other", "compare");
|
||||||
addEndpointToGroup("Other", "add-page-numbers");
|
addEndpointToGroup("Other", "add-page-numbers");
|
||||||
addEndpointToGroup("Other", "auto-rename");
|
addEndpointToGroup("Other", "auto-rename");
|
||||||
addEndpointToGroup("Other", "get-info-on-pdf");
|
addEndpointToGroup("Other", "get-info-on-pdf");
|
||||||
addEndpointToGroup("Other", "show-javascript");
|
addEndpointToGroup("Other", "show-javascript");
|
||||||
|
|
||||||
// CLI
|
// CLI
|
||||||
addEndpointToGroup("CLI", "compress-pdf");
|
addEndpointToGroup("CLI", "compress-pdf");
|
||||||
addEndpointToGroup("CLI", "extract-image-scans");
|
addEndpointToGroup("CLI", "extract-image-scans");
|
||||||
addEndpointToGroup("CLI", "remove-blanks");
|
addEndpointToGroup("CLI", "remove-blanks");
|
||||||
addEndpointToGroup("CLI", "repair");
|
addEndpointToGroup("CLI", "repair");
|
||||||
addEndpointToGroup("CLI", "pdf-to-pdfa");
|
addEndpointToGroup("CLI", "pdf-to-pdfa");
|
||||||
addEndpointToGroup("CLI", "file-to-pdf");
|
addEndpointToGroup("CLI", "file-to-pdf");
|
||||||
addEndpointToGroup("CLI", "xlsx-to-pdf");
|
addEndpointToGroup("CLI", "xlsx-to-pdf");
|
||||||
addEndpointToGroup("CLI", "pdf-to-word");
|
addEndpointToGroup("CLI", "pdf-to-word");
|
||||||
addEndpointToGroup("CLI", "pdf-to-presentation");
|
addEndpointToGroup("CLI", "pdf-to-presentation");
|
||||||
addEndpointToGroup("CLI", "pdf-to-text");
|
addEndpointToGroup("CLI", "pdf-to-text");
|
||||||
addEndpointToGroup("CLI", "pdf-to-html");
|
addEndpointToGroup("CLI", "pdf-to-html");
|
||||||
addEndpointToGroup("CLI", "pdf-to-xml");
|
addEndpointToGroup("CLI", "pdf-to-xml");
|
||||||
addEndpointToGroup("CLI", "ocr-pdf");
|
addEndpointToGroup("CLI", "ocr-pdf");
|
||||||
addEndpointToGroup("CLI", "html-to-pdf");
|
addEndpointToGroup("CLI", "html-to-pdf");
|
||||||
addEndpointToGroup("CLI", "url-to-pdf");
|
addEndpointToGroup("CLI", "url-to-pdf");
|
||||||
|
|
||||||
// python
|
// python
|
||||||
addEndpointToGroup("Python", "extract-image-scans");
|
addEndpointToGroup("Python", "extract-image-scans");
|
||||||
addEndpointToGroup("Python", "remove-blanks");
|
addEndpointToGroup("Python", "remove-blanks");
|
||||||
addEndpointToGroup("Python", "html-to-pdf");
|
addEndpointToGroup("Python", "html-to-pdf");
|
||||||
addEndpointToGroup("Python", "url-to-pdf");
|
addEndpointToGroup("Python", "url-to-pdf");
|
||||||
|
|
||||||
// openCV
|
// openCV
|
||||||
addEndpointToGroup("OpenCV", "extract-image-scans");
|
addEndpointToGroup("OpenCV", "extract-image-scans");
|
||||||
addEndpointToGroup("OpenCV", "remove-blanks");
|
addEndpointToGroup("OpenCV", "remove-blanks");
|
||||||
|
|
||||||
// LibreOffice
|
// LibreOffice
|
||||||
addEndpointToGroup("LibreOffice", "repair");
|
addEndpointToGroup("LibreOffice", "repair");
|
||||||
addEndpointToGroup("LibreOffice", "file-to-pdf");
|
addEndpointToGroup("LibreOffice", "file-to-pdf");
|
||||||
addEndpointToGroup("LibreOffice", "xlsx-to-pdf");
|
addEndpointToGroup("LibreOffice", "xlsx-to-pdf");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-word");
|
addEndpointToGroup("LibreOffice", "pdf-to-word");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
|
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-text");
|
addEndpointToGroup("LibreOffice", "pdf-to-text");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-html");
|
addEndpointToGroup("LibreOffice", "pdf-to-html");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-xml");
|
addEndpointToGroup("LibreOffice", "pdf-to-xml");
|
||||||
|
|
||||||
// OCRmyPDF
|
// OCRmyPDF
|
||||||
addEndpointToGroup("OCRmyPDF", "compress-pdf");
|
addEndpointToGroup("OCRmyPDF", "compress-pdf");
|
||||||
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa");
|
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa");
|
||||||
addEndpointToGroup("OCRmyPDF", "ocr-pdf");
|
addEndpointToGroup("OCRmyPDF", "ocr-pdf");
|
||||||
|
|
||||||
// Java
|
// Java
|
||||||
addEndpointToGroup("Java", "merge-pdfs");
|
addEndpointToGroup("Java", "merge-pdfs");
|
||||||
addEndpointToGroup("Java", "remove-pages");
|
addEndpointToGroup("Java", "remove-pages");
|
||||||
addEndpointToGroup("Java", "split-pdfs");
|
addEndpointToGroup("Java", "split-pdfs");
|
||||||
addEndpointToGroup("Java", "pdf-organizer");
|
addEndpointToGroup("Java", "pdf-organizer");
|
||||||
addEndpointToGroup("Java", "rotate-pdf");
|
addEndpointToGroup("Java", "rotate-pdf");
|
||||||
addEndpointToGroup("Java", "pdf-to-img");
|
addEndpointToGroup("Java", "pdf-to-img");
|
||||||
addEndpointToGroup("Java", "img-to-pdf");
|
addEndpointToGroup("Java", "img-to-pdf");
|
||||||
addEndpointToGroup("Java", "add-password");
|
addEndpointToGroup("Java", "add-password");
|
||||||
addEndpointToGroup("Java", "remove-password");
|
addEndpointToGroup("Java", "remove-password");
|
||||||
addEndpointToGroup("Java", "change-permissions");
|
addEndpointToGroup("Java", "change-permissions");
|
||||||
addEndpointToGroup("Java", "add-watermark");
|
addEndpointToGroup("Java", "add-watermark");
|
||||||
addEndpointToGroup("Java", "add-image");
|
addEndpointToGroup("Java", "add-image");
|
||||||
addEndpointToGroup("Java", "extract-images");
|
addEndpointToGroup("Java", "extract-images");
|
||||||
addEndpointToGroup("Java", "change-metadata");
|
addEndpointToGroup("Java", "change-metadata");
|
||||||
addEndpointToGroup("Java", "cert-sign");
|
addEndpointToGroup("Java", "cert-sign");
|
||||||
addEndpointToGroup("Java", "multi-page-layout");
|
addEndpointToGroup("Java", "multi-page-layout");
|
||||||
addEndpointToGroup("Java", "scale-pages");
|
addEndpointToGroup("Java", "scale-pages");
|
||||||
addEndpointToGroup("Java", "add-page-numbers");
|
addEndpointToGroup("Java", "add-page-numbers");
|
||||||
addEndpointToGroup("Java", "auto-rename");
|
addEndpointToGroup("Java", "auto-rename");
|
||||||
addEndpointToGroup("Java", "auto-split-pdf");
|
addEndpointToGroup("Java", "auto-split-pdf");
|
||||||
addEndpointToGroup("Java", "sanitize-pdf");
|
addEndpointToGroup("Java", "sanitize-pdf");
|
||||||
addEndpointToGroup("Java", "crop");
|
addEndpointToGroup("Java", "crop");
|
||||||
addEndpointToGroup("Java", "get-info-on-pdf");
|
addEndpointToGroup("Java", "get-info-on-pdf");
|
||||||
addEndpointToGroup("Java", "extract-page");
|
addEndpointToGroup("Java", "extract-page");
|
||||||
addEndpointToGroup("Java", "pdf-to-single-page");
|
addEndpointToGroup("Java", "pdf-to-single-page");
|
||||||
addEndpointToGroup("Java", "markdown-to-pdf");
|
addEndpointToGroup("Java", "markdown-to-pdf");
|
||||||
addEndpointToGroup("Java", "show-javascript");
|
addEndpointToGroup("Java", "show-javascript");
|
||||||
addEndpointToGroup("Java", "auto-redact");
|
addEndpointToGroup("Java", "auto-redact");
|
||||||
addEndpointToGroup("Java", "pdf-to-csv");
|
addEndpointToGroup("Java", "pdf-to-csv");
|
||||||
addEndpointToGroup("Java", "split-by-size-or-count");
|
addEndpointToGroup("Java", "split-by-size-or-count");
|
||||||
addEndpointToGroup("Java", "overlay-pdf");
|
addEndpointToGroup("Java", "overlay-pdf");
|
||||||
addEndpointToGroup("Java", "split-pdf-by-sections");
|
addEndpointToGroup("Java", "split-pdf-by-sections");
|
||||||
|
|
||||||
// Javascript
|
// Javascript
|
||||||
addEndpointToGroup("Javascript", "pdf-organizer");
|
addEndpointToGroup("Javascript", "pdf-organizer");
|
||||||
addEndpointToGroup("Javascript", "sign");
|
addEndpointToGroup("Javascript", "sign");
|
||||||
addEndpointToGroup("Javascript", "compare");
|
addEndpointToGroup("Javascript", "compare");
|
||||||
addEndpointToGroup("Javascript", "adjust-contrast");
|
addEndpointToGroup("Javascript", "adjust-contrast");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processEnvironmentConfigs() {
|
private void processEnvironmentConfigs() {
|
||||||
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
||||||
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
||||||
|
|
||||||
if (endpointsToRemove != null) {
|
if (endpointsToRemove != null) {
|
||||||
for (String endpoint : endpointsToRemove) {
|
for (String endpoint : endpointsToRemove) {
|
||||||
disableEndpoint(endpoint.trim());
|
disableEndpoint(endpoint.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupsToRemove != null) {
|
if (groupsToRemove != null) {
|
||||||
for (String group : groupsToRemove) {
|
for (String group : groupsToRemove) {
|
||||||
disableGroup(group.trim());
|
disableGroup(group.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class EndpointInterceptor implements HandlerInterceptor {
|
public class EndpointInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
@Autowired private EndpointConfiguration endpointConfiguration;
|
@Autowired private EndpointConfiguration endpointConfiguration;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(
|
public boolean preHandle(
|
||||||
HttpServletRequest request, HttpServletResponse response, Object handler)
|
HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
if (!endpointConfiguration.isEndpointEnabled(requestURI)) {
|
if (!endpointConfiguration.isEndpointEnabled(requestURI)) {
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "This endpoint is disabled");
|
response.sendError(HttpServletResponse.SC_FORBIDDEN, "This endpoint is disabled");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import io.micrometer.core.instrument.Meter;
|
import io.micrometer.core.instrument.Meter;
|
||||||
import io.micrometer.core.instrument.config.MeterFilter;
|
import io.micrometer.core.instrument.config.MeterFilter;
|
||||||
import io.micrometer.core.instrument.config.MeterFilterReply;
|
import io.micrometer.core.instrument.config.MeterFilterReply;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class MetricsConfig {
|
public class MetricsConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public MeterFilter meterFilter() {
|
public MeterFilter meterFilter() {
|
||||||
return new MeterFilter() {
|
return new MeterFilter() {
|
||||||
@Override
|
@Override
|
||||||
public MeterFilterReply accept(Meter.Id id) {
|
public MeterFilterReply accept(Meter.Id id) {
|
||||||
if (id.getName().equals("http.requests")) {
|
if (id.getName().equals("http.requests")) {
|
||||||
return MeterFilterReply.NEUTRAL;
|
return MeterFilterReply.NEUTRAL;
|
||||||
}
|
}
|
||||||
return MeterFilterReply.DENY;
|
return MeterFilterReply.DENY;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,64 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import io.micrometer.core.instrument.Counter;
|
import io.micrometer.core.instrument.Counter;
|
||||||
import io.micrometer.core.instrument.MeterRegistry;
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class MetricsFilter extends OncePerRequestFilter {
|
public class MetricsFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final MeterRegistry meterRegistry;
|
private final MeterRegistry meterRegistry;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public MetricsFilter(MeterRegistry meterRegistry) {
|
public MetricsFilter(MeterRegistry meterRegistry) {
|
||||||
this.meterRegistry = meterRegistry;
|
this.meterRegistry = meterRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(
|
protected void doFilterInternal(
|
||||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
|
|
||||||
// System.out.println("uri="+uri + ", method=" + request.getMethod() );
|
// System.out.println("uri="+uri + ", method=" + request.getMethod() );
|
||||||
// Ignore static resources
|
// Ignore static resources
|
||||||
if (!(uri.startsWith("/js")
|
if (!(uri.startsWith("/js")
|
||||||
|| uri.startsWith("/v1/api-docs")
|
|| uri.startsWith("/v1/api-docs")
|
||||||
|| uri.endsWith("robots.txt")
|
|| uri.endsWith("robots.txt")
|
||||||
|| uri.startsWith("/images")
|
|| uri.startsWith("/images")
|
||||||
|| uri.startsWith("/images")
|
|| uri.startsWith("/images")
|
||||||
|| uri.endsWith(".png")
|
|| uri.endsWith(".png")
|
||||||
|| uri.endsWith(".ico")
|
|| uri.endsWith(".ico")
|
||||||
|| uri.endsWith(".css")
|
|| uri.endsWith(".css")
|
||||||
|| uri.endsWith(".map")
|
|| uri.endsWith(".map")
|
||||||
|| uri.endsWith(".svg")
|
|| uri.endsWith(".svg")
|
||||||
|| uri.endsWith(".js")
|
|| uri.endsWith(".js")
|
||||||
|| uri.contains("swagger")
|
|| uri.contains("swagger")
|
||||||
|| uri.startsWith("/api/v1/info")
|
|| uri.startsWith("/api/v1/info")
|
||||||
|| uri.startsWith("/site.webmanifest")
|
|| uri.startsWith("/site.webmanifest")
|
||||||
|| uri.startsWith("/fonts")
|
|| uri.startsWith("/fonts")
|
||||||
|| uri.startsWith("/pdfjs"))) {
|
|| uri.startsWith("/pdfjs"))) {
|
||||||
|
|
||||||
Counter counter =
|
Counter counter =
|
||||||
Counter.builder("http.requests")
|
Counter.builder("http.requests")
|
||||||
.tag("uri", uri)
|
.tag("uri", uri)
|
||||||
.tag("method", request.getMethod())
|
.tag("method", request.getMethod())
|
||||||
.register(meterRegistry);
|
.register(meterRegistry);
|
||||||
|
|
||||||
counter.increment();
|
counter.increment();
|
||||||
// System.out.println("Counted");
|
// System.out.println("Counted");
|
||||||
}
|
}
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,53 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import io.swagger.v3.oas.models.Components;
|
import io.swagger.v3.oas.models.Components;
|
||||||
import io.swagger.v3.oas.models.OpenAPI;
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
import io.swagger.v3.oas.models.info.Info;
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class OpenApiConfig {
|
public class OpenApiConfig {
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public OpenAPI customOpenAPI() {
|
public OpenAPI customOpenAPI() {
|
||||||
String version = getClass().getPackage().getImplementationVersion();
|
String version = getClass().getPackage().getImplementationVersion();
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
version = "1.0.0"; // default version if all else fails
|
version = "1.0.0"; // default version if all else fails
|
||||||
}
|
}
|
||||||
|
|
||||||
SecurityScheme apiKeyScheme =
|
SecurityScheme apiKeyScheme =
|
||||||
new SecurityScheme()
|
new SecurityScheme()
|
||||||
.type(SecurityScheme.Type.APIKEY)
|
.type(SecurityScheme.Type.APIKEY)
|
||||||
.in(SecurityScheme.In.HEADER)
|
.in(SecurityScheme.In.HEADER)
|
||||||
.name("X-API-KEY");
|
.name("X-API-KEY");
|
||||||
if (!applicationProperties.getSecurity().getEnableLogin()) {
|
if (!applicationProperties.getSecurity().getEnableLogin()) {
|
||||||
return new OpenAPI()
|
return new OpenAPI()
|
||||||
.components(new Components())
|
.components(new Components())
|
||||||
.info(
|
.info(
|
||||||
new Info()
|
new Info()
|
||||||
.title("Stirling PDF API")
|
.title("Stirling PDF API")
|
||||||
.version(version)
|
.version(version)
|
||||||
.description(
|
.description(
|
||||||
"API documentation for all Server-Side processing.\nPlease note some functionality might be UI only and missing from here."));
|
"API documentation for all Server-Side processing.\nPlease note some functionality might be UI only and missing from here."));
|
||||||
} else {
|
} else {
|
||||||
return new OpenAPI()
|
return new OpenAPI()
|
||||||
.components(new Components().addSecuritySchemes("apiKey", apiKeyScheme))
|
.components(new Components().addSecuritySchemes("apiKey", apiKeyScheme))
|
||||||
.info(
|
.info(
|
||||||
new Info()
|
new Info()
|
||||||
.title("Stirling PDF API")
|
.title("Stirling PDF API")
|
||||||
.version(version)
|
.version(version)
|
||||||
.description(
|
.description(
|
||||||
"API documentation for all Server-Side processing.\nPlease note some functionality might be UI only and missing from here."))
|
"API documentation for all Server-Side processing.\nPlease note some functionality might be UI only and missing from here."))
|
||||||
.addSecurityItem(new SecurityRequirement().addList("apiKey"));
|
.addSecurityItem(new SecurityRequirement().addList("apiKey"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.context.event.ContextRefreshedEvent;
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
|
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||||
|
|
||||||
public static LocalDateTime startTime;
|
public static LocalDateTime startTime;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||||
startTime = LocalDateTime.now();
|
startTime = LocalDateTime.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class WebMvcConfig implements WebMvcConfigurer {
|
public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Autowired private EndpointInterceptor endpointInterceptor;
|
@Autowired private EndpointInterceptor endpointInterceptor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(endpointInterceptor);
|
registry.addInterceptor(endpointInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
@Autowired private final LoginAttemptService loginAttemptService;
|
@Autowired private final LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public CustomAuthenticationFailureHandler(LoginAttemptService loginAttemptService) {
|
public CustomAuthenticationFailureHandler(LoginAttemptService loginAttemptService) {
|
||||||
this.loginAttemptService = loginAttemptService;
|
this.loginAttemptService = loginAttemptService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticationFailure(
|
public void onAuthenticationFailure(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
AuthenticationException exception)
|
AuthenticationException exception)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
String ip = request.getRemoteAddr();
|
String ip = request.getRemoteAddr();
|
||||||
logger.error("Failed login attempt from IP: " + ip);
|
logger.error("Failed login attempt from IP: " + ip);
|
||||||
|
|
||||||
String username = request.getParameter("username");
|
String username = request.getParameter("username");
|
||||||
if (loginAttemptService.loginAttemptCheck(username)) {
|
if (loginAttemptService.loginAttemptCheck(username)) {
|
||||||
setDefaultFailureUrl("/login?error=locked");
|
setDefaultFailureUrl("/login?error=locked");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
||||||
setDefaultFailureUrl("/login?error=badcredentials");
|
setDefaultFailureUrl("/login?error=badcredentials");
|
||||||
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||||
setDefaultFailureUrl("/login?error=locked");
|
setDefaultFailureUrl("/login?error=locked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onAuthenticationFailure(request, response, exception);
|
super.onAuthenticationFailure(request, response, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +1,57 @@
|
||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.Authority;
|
import stirling.software.SPDF.model.Authority;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
import stirling.software.SPDF.repository.UserRepository;
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CustomUserDetailsService implements UserDetailsService {
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired private LoginAttemptService loginAttemptService;
|
@Autowired private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
User user =
|
User user =
|
||||||
userRepository
|
userRepository
|
||||||
.findByUsername(username)
|
.findByUsername(username)
|
||||||
.orElseThrow(
|
.orElseThrow(
|
||||||
() ->
|
() ->
|
||||||
new UsernameNotFoundException(
|
new UsernameNotFoundException(
|
||||||
"No user found with username: " + username));
|
"No user found with username: " + username));
|
||||||
|
|
||||||
if (loginAttemptService.isBlocked(username)) {
|
if (loginAttemptService.isBlocked(username)) {
|
||||||
throw new LockedException(
|
throw new LockedException(
|
||||||
"Your account has been locked due to too many failed login attempts.");
|
"Your account has been locked due to too many failed login attempts.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new org.springframework.security.core.userdetails.User(
|
return new org.springframework.security.core.userdetails.User(
|
||||||
user.getUsername(),
|
user.getUsername(),
|
||||||
user.getPassword(),
|
user.getPassword(),
|
||||||
user.isEnabled(),
|
user.isEnabled(),
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
getAuthorities(user.getAuthorities()));
|
getAuthorities(user.getAuthorities()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<? extends GrantedAuthority> getAuthorities(Set<Authority> authorities) {
|
private Collection<? extends GrantedAuthority> getAuthorities(Set<Authority> authorities) {
|
||||||
return authorities.stream()
|
return authorities.stream()
|
||||||
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
|
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,140 +1,140 @@
|
||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity()
|
@EnableWebSecurity()
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
@Autowired private UserDetailsService userDetailsService;
|
@Autowired private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PasswordEncoder passwordEncoder() {
|
public PasswordEncoder passwordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired @Lazy private UserService userService;
|
@Autowired @Lazy private UserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("loginEnabled")
|
@Qualifier("loginEnabled")
|
||||||
public boolean loginEnabledValue;
|
public boolean loginEnabledValue;
|
||||||
|
|
||||||
@Autowired private UserAuthenticationFilter userAuthenticationFilter;
|
@Autowired private UserAuthenticationFilter userAuthenticationFilter;
|
||||||
|
|
||||||
@Autowired private LoginAttemptService loginAttemptService;
|
@Autowired private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
@Autowired private FirstLoginFilter firstLoginFilter;
|
@Autowired private FirstLoginFilter firstLoginFilter;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
if (loginEnabledValue) {
|
if (loginEnabledValue) {
|
||||||
|
|
||||||
http.csrf(csrf -> csrf.disable());
|
http.csrf(csrf -> csrf.disable());
|
||||||
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
http.formLogin(
|
http.formLogin(
|
||||||
formLogin ->
|
formLogin ->
|
||||||
formLogin
|
formLogin
|
||||||
.loginPage("/login")
|
.loginPage("/login")
|
||||||
.successHandler(
|
.successHandler(
|
||||||
new CustomAuthenticationSuccessHandler())
|
new CustomAuthenticationSuccessHandler())
|
||||||
.defaultSuccessUrl("/")
|
.defaultSuccessUrl("/")
|
||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomAuthenticationFailureHandler(
|
new CustomAuthenticationFailureHandler(
|
||||||
loginAttemptService))
|
loginAttemptService))
|
||||||
.permitAll())
|
.permitAll())
|
||||||
.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()))
|
.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()))
|
||||||
.logout(
|
.logout(
|
||||||
logout ->
|
logout ->
|
||||||
logout.logoutRequestMatcher(
|
logout.logoutRequestMatcher(
|
||||||
new AntPathRequestMatcher("/logout"))
|
new AntPathRequestMatcher("/logout"))
|
||||||
.logoutSuccessUrl("/login?logout=true")
|
.logoutSuccessUrl("/login?logout=true")
|
||||||
.invalidateHttpSession(true) // Invalidate session
|
.invalidateHttpSession(true) // Invalidate session
|
||||||
.deleteCookies("JSESSIONID", "remember-me"))
|
.deleteCookies("JSESSIONID", "remember-me"))
|
||||||
.rememberMe(
|
.rememberMe(
|
||||||
rememberMeConfigurer ->
|
rememberMeConfigurer ->
|
||||||
rememberMeConfigurer // Use the configurator directly
|
rememberMeConfigurer // Use the configurator directly
|
||||||
.key("uniqueAndSecret")
|
.key("uniqueAndSecret")
|
||||||
.tokenRepository(persistentTokenRepository())
|
.tokenRepository(persistentTokenRepository())
|
||||||
.tokenValiditySeconds(1209600) // 2 weeks
|
.tokenValiditySeconds(1209600) // 2 weeks
|
||||||
)
|
)
|
||||||
.authorizeHttpRequests(
|
.authorizeHttpRequests(
|
||||||
authz ->
|
authz ->
|
||||||
authz.requestMatchers(
|
authz.requestMatchers(
|
||||||
req -> {
|
req -> {
|
||||||
String uri = req.getRequestURI();
|
String uri = req.getRequestURI();
|
||||||
String contextPath = req.getContextPath();
|
String contextPath = req.getContextPath();
|
||||||
|
|
||||||
// Remove the context path from the URI
|
// Remove the context path from the URI
|
||||||
String trimmedUri =
|
String trimmedUri =
|
||||||
uri.startsWith(contextPath)
|
uri.startsWith(contextPath)
|
||||||
? uri.substring(
|
? uri.substring(
|
||||||
contextPath
|
contextPath
|
||||||
.length())
|
.length())
|
||||||
: uri;
|
: uri;
|
||||||
|
|
||||||
return trimmedUri.startsWith("/login")
|
return trimmedUri.startsWith("/login")
|
||||||
|| trimmedUri.endsWith(".svg")
|
|| trimmedUri.endsWith(".svg")
|
||||||
|| trimmedUri.startsWith(
|
|| trimmedUri.startsWith(
|
||||||
"/register")
|
"/register")
|
||||||
|| trimmedUri.startsWith("/error")
|
|| trimmedUri.startsWith("/error")
|
||||||
|| trimmedUri.startsWith("/images/")
|
|| trimmedUri.startsWith("/images/")
|
||||||
|| trimmedUri.startsWith("/public/")
|
|| trimmedUri.startsWith("/public/")
|
||||||
|| trimmedUri.startsWith("/css/")
|
|| trimmedUri.startsWith("/css/")
|
||||||
|| trimmedUri.startsWith("/js/")
|
|| trimmedUri.startsWith("/js/")
|
||||||
|| trimmedUri.startsWith(
|
|| trimmedUri.startsWith(
|
||||||
"/api/v1/info/status");
|
"/api/v1/info/status");
|
||||||
})
|
})
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.anyRequest()
|
.anyRequest()
|
||||||
.authenticated())
|
.authenticated())
|
||||||
.userDetailsService(userDetailsService)
|
.userDetailsService(userDetailsService)
|
||||||
.authenticationProvider(authenticationProvider());
|
.authenticationProvider(authenticationProvider());
|
||||||
} else {
|
} else {
|
||||||
http.csrf(csrf -> csrf.disable())
|
http.csrf(csrf -> csrf.disable())
|
||||||
.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public IPRateLimitingFilter rateLimitingFilter() {
|
public IPRateLimitingFilter rateLimitingFilter() {
|
||||||
int maxRequestsPerIp = 1000000; // Example limit TODO add config level
|
int maxRequestsPerIp = 1000000; // Example limit TODO add config level
|
||||||
return new IPRateLimitingFilter(maxRequestsPerIp, maxRequestsPerIp);
|
return new IPRateLimitingFilter(maxRequestsPerIp, maxRequestsPerIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public DaoAuthenticationProvider authenticationProvider() {
|
public DaoAuthenticationProvider authenticationProvider() {
|
||||||
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||||
authProvider.setUserDetailsService(userDetailsService);
|
authProvider.setUserDetailsService(userDetailsService);
|
||||||
authProvider.setPasswordEncoder(passwordEncoder());
|
authProvider.setPasswordEncoder(passwordEncoder());
|
||||||
return authProvider;
|
return authProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PersistentTokenRepository persistentTokenRepository() {
|
public PersistentTokenRepository persistentTokenRepository() {
|
||||||
return new JPATokenRepositoryImpl();
|
return new JPATokenRepositoryImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,118 +1,118 @@
|
||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
@Autowired private UserDetailsService userDetailsService;
|
@Autowired private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
@Autowired @Lazy private UserService userService;
|
@Autowired @Lazy private UserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("loginEnabled")
|
@Qualifier("loginEnabled")
|
||||||
public boolean loginEnabledValue;
|
public boolean loginEnabledValue;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(
|
protected void doFilterInternal(
|
||||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
if (!loginEnabledValue) {
|
if (!loginEnabledValue) {
|
||||||
// If login is not enabled, just pass all requests without authentication
|
// If login is not enabled, just pass all requests without authentication
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
// Check for API key in the request headers if no authentication exists
|
// Check for API key in the request headers if no authentication exists
|
||||||
if (authentication == null || !authentication.isAuthenticated()) {
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
String apiKey = request.getHeader("X-API-Key");
|
String apiKey = request.getHeader("X-API-Key");
|
||||||
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
||||||
try {
|
try {
|
||||||
// Use API key to authenticate. This requires you to have an authentication
|
// Use API key to authenticate. This requires you to have an authentication
|
||||||
// provider for API keys.
|
// provider for API keys.
|
||||||
UserDetails userDetails = userService.loadUserByApiKey(apiKey);
|
UserDetails userDetails = userService.loadUserByApiKey(apiKey);
|
||||||
if (userDetails == null) {
|
if (userDetails == null) {
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
response.getWriter().write("Invalid API Key.");
|
response.getWriter().write("Invalid API Key.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
authentication =
|
authentication =
|
||||||
new ApiKeyAuthenticationToken(
|
new ApiKeyAuthenticationToken(
|
||||||
userDetails, apiKey, userDetails.getAuthorities());
|
userDetails, apiKey, userDetails.getAuthorities());
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
} catch (AuthenticationException e) {
|
} catch (AuthenticationException e) {
|
||||||
// If API key authentication fails, deny the request
|
// If API key authentication fails, deny the request
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
response.getWriter().write("Invalid API Key.");
|
response.getWriter().write("Invalid API Key.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we still don't have any authentication, deny the request
|
// If we still don't have any authentication, deny the request
|
||||||
if (authentication == null || !authentication.isAuthenticated()) {
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
String method = request.getMethod();
|
String method = request.getMethod();
|
||||||
String contextPath = request.getContextPath();
|
String contextPath = request.getContextPath();
|
||||||
|
|
||||||
if ("GET".equalsIgnoreCase(method) && !(contextPath + "/login").equals(requestURI)) {
|
if ("GET".equalsIgnoreCase(method) && !(contextPath + "/login").equals(requestURI)) {
|
||||||
response.sendRedirect(contextPath + "/login"); // redirect to the login page
|
response.sendRedirect(contextPath + "/login"); // redirect to the login page
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
response.getWriter()
|
response.getWriter()
|
||||||
.write(
|
.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");
|
"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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
String contextPath = request.getContextPath();
|
String contextPath = request.getContextPath();
|
||||||
String[] permitAllPatterns = {
|
String[] permitAllPatterns = {
|
||||||
contextPath + "/login",
|
contextPath + "/login",
|
||||||
contextPath + "/register",
|
contextPath + "/register",
|
||||||
contextPath + "/error",
|
contextPath + "/error",
|
||||||
contextPath + "/images/",
|
contextPath + "/images/",
|
||||||
contextPath + "/public/",
|
contextPath + "/public/",
|
||||||
contextPath + "/css/",
|
contextPath + "/css/",
|
||||||
contextPath + "/js/",
|
contextPath + "/js/",
|
||||||
contextPath + "/pdfjs/",
|
contextPath + "/pdfjs/",
|
||||||
contextPath + "/api/v1/info/status",
|
contextPath + "/api/v1/info/status",
|
||||||
contextPath + "/site.webmanifest"
|
contextPath + "/site.webmanifest"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String pattern : permitAllPatterns) {
|
for (String pattern : permitAllPatterns) {
|
||||||
if (uri.startsWith(pattern) || uri.endsWith(".svg")) {
|
if (uri.startsWith(pattern) || uri.endsWith(".svg")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,143 +1,143 @@
|
||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import io.github.bucket4j.Bandwidth;
|
import io.github.bucket4j.Bandwidth;
|
||||||
import io.github.bucket4j.Bucket;
|
import io.github.bucket4j.Bucket;
|
||||||
import io.github.bucket4j.ConsumptionProbe;
|
import io.github.bucket4j.ConsumptionProbe;
|
||||||
import io.github.bucket4j.Refill;
|
import io.github.bucket4j.Refill;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
|
public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final Map<String, Bucket> apiBuckets = new ConcurrentHashMap<>();
|
private final Map<String, Bucket> apiBuckets = new ConcurrentHashMap<>();
|
||||||
private final Map<String, Bucket> webBuckets = new ConcurrentHashMap<>();
|
private final Map<String, Bucket> webBuckets = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Autowired private UserDetailsService userDetailsService;
|
@Autowired private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("rateLimit")
|
@Qualifier("rateLimit")
|
||||||
public boolean rateLimit;
|
public boolean rateLimit;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(
|
protected void doFilterInternal(
|
||||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
if (!rateLimit) {
|
if (!rateLimit) {
|
||||||
// If rateLimit is not enabled, just pass all requests without rate limiting
|
// If rateLimit is not enabled, just pass all requests without rate limiting
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String method = request.getMethod();
|
String method = request.getMethod();
|
||||||
if (!"POST".equalsIgnoreCase(method)) {
|
if (!"POST".equalsIgnoreCase(method)) {
|
||||||
// If the request is not a POST, just pass it through without rate limiting
|
// If the request is not a POST, just pass it through without rate limiting
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String identifier = null;
|
String identifier = null;
|
||||||
|
|
||||||
// Check for API key in the request headers
|
// Check for API key in the request headers
|
||||||
String apiKey = request.getHeader("X-API-Key");
|
String apiKey = request.getHeader("X-API-Key");
|
||||||
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
||||||
identifier =
|
identifier =
|
||||||
"API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames
|
"API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames
|
||||||
} else {
|
} else {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
identifier = userDetails.getUsername();
|
identifier = userDetails.getUsername();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If neither API key nor an authenticated user is present, use IP address
|
// If neither API key nor an authenticated user is present, use IP address
|
||||||
if (identifier == null) {
|
if (identifier == null) {
|
||||||
identifier = request.getRemoteAddr();
|
identifier = request.getRemoteAddr();
|
||||||
}
|
}
|
||||||
|
|
||||||
Role userRole =
|
Role userRole =
|
||||||
getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication());
|
getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
|
||||||
if (request.getHeader("X-API-Key") != null) {
|
if (request.getHeader("X-API-Key") != null) {
|
||||||
// It's an API call
|
// It's an API call
|
||||||
processRequest(
|
processRequest(
|
||||||
userRole.getApiCallsPerDay(),
|
userRole.getApiCallsPerDay(),
|
||||||
identifier,
|
identifier,
|
||||||
apiBuckets,
|
apiBuckets,
|
||||||
request,
|
request,
|
||||||
response,
|
response,
|
||||||
filterChain);
|
filterChain);
|
||||||
} else {
|
} else {
|
||||||
// It's a Web UI call
|
// It's a Web UI call
|
||||||
processRequest(
|
processRequest(
|
||||||
userRole.getWebCallsPerDay(),
|
userRole.getWebCallsPerDay(),
|
||||||
identifier,
|
identifier,
|
||||||
webBuckets,
|
webBuckets,
|
||||||
request,
|
request,
|
||||||
response,
|
response,
|
||||||
filterChain);
|
filterChain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Role getRoleFromAuthentication(Authentication authentication) {
|
private Role getRoleFromAuthentication(Authentication authentication) {
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
||||||
try {
|
try {
|
||||||
return Role.fromString(authority.getAuthority());
|
return Role.fromString(authority.getAuthority());
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
// Ignore and continue to next authority.
|
// Ignore and continue to next authority.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("User does not have a valid role.");
|
throw new IllegalStateException("User does not have a valid role.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processRequest(
|
private void processRequest(
|
||||||
int limitPerDay,
|
int limitPerDay,
|
||||||
String identifier,
|
String identifier,
|
||||||
Map<String, Bucket> buckets,
|
Map<String, Bucket> buckets,
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
FilterChain filterChain)
|
FilterChain filterChain)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
Bucket userBucket = buckets.computeIfAbsent(identifier, k -> createUserBucket(limitPerDay));
|
Bucket userBucket = buckets.computeIfAbsent(identifier, k -> createUserBucket(limitPerDay));
|
||||||
ConsumptionProbe probe = userBucket.tryConsumeAndReturnRemaining(1);
|
ConsumptionProbe probe = userBucket.tryConsumeAndReturnRemaining(1);
|
||||||
|
|
||||||
if (probe.isConsumed()) {
|
if (probe.isConsumed()) {
|
||||||
response.setHeader("X-Rate-Limit-Remaining", Long.toString(probe.getRemainingTokens()));
|
response.setHeader("X-Rate-Limit-Remaining", Long.toString(probe.getRemainingTokens()));
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
} else {
|
} else {
|
||||||
long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000;
|
long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000;
|
||||||
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
|
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
|
||||||
response.setHeader("X-Rate-Limit-Retry-After-Seconds", String.valueOf(waitForRefill));
|
response.setHeader("X-Rate-Limit-Retry-After-Seconds", String.valueOf(waitForRefill));
|
||||||
response.getWriter().write("Rate limit exceeded for POST requests.");
|
response.getWriter().write("Rate limit exceeded for POST requests.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bucket createUserBucket(int limitPerDay) {
|
private Bucket createUserBucket(int limitPerDay) {
|
||||||
Bandwidth limit =
|
Bandwidth limit =
|
||||||
Bandwidth.classic(limitPerDay, Refill.intervally(limitPerDay, Duration.ofDays(1)));
|
Bandwidth.classic(limitPerDay, Refill.intervally(limitPerDay, Duration.ofDays(1)));
|
||||||
return Bucket.builder().addLimit(limit).build();
|
return Bucket.builder().addLimit(limit).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,197 +1,197 @@
|
||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||||
import stirling.software.SPDF.model.Authority;
|
import stirling.software.SPDF.model.Authority;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
import stirling.software.SPDF.repository.UserRepository;
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserService implements UserServiceInterface {
|
public class UserService implements UserServiceInterface {
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired private PasswordEncoder passwordEncoder;
|
@Autowired private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
public Authentication getAuthentication(String apiKey) {
|
public Authentication getAuthentication(String apiKey) {
|
||||||
User user = getUserByApiKey(apiKey);
|
User user = getUserByApiKey(apiKey);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new UsernameNotFoundException("API key is not valid");
|
throw new UsernameNotFoundException("API key is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the user into an Authentication object
|
// Convert the user into an Authentication object
|
||||||
return new UsernamePasswordAuthenticationToken(
|
return new UsernamePasswordAuthenticationToken(
|
||||||
user, // principal (typically the user)
|
user, // principal (typically the user)
|
||||||
null, // credentials (we don't expose the password or API key here)
|
null, // credentials (we don't expose the password or API key here)
|
||||||
getAuthorities(user) // user's authorities (roles/permissions)
|
getAuthorities(user) // user's authorities (roles/permissions)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
|
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
|
||||||
// Convert each Authority object into a SimpleGrantedAuthority object.
|
// Convert each Authority object into a SimpleGrantedAuthority object.
|
||||||
return user.getAuthorities().stream()
|
return user.getAuthorities().stream()
|
||||||
.map((Authority authority) -> new SimpleGrantedAuthority(authority.getAuthority()))
|
.map((Authority authority) -> new SimpleGrantedAuthority(authority.getAuthority()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateApiKey() {
|
private String generateApiKey() {
|
||||||
String apiKey;
|
String apiKey;
|
||||||
do {
|
do {
|
||||||
apiKey = UUID.randomUUID().toString();
|
apiKey = UUID.randomUUID().toString();
|
||||||
} while (userRepository.findByApiKey(apiKey) != null); // Ensure uniqueness
|
} while (userRepository.findByApiKey(apiKey) != null); // Ensure uniqueness
|
||||||
return apiKey;
|
return apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User addApiKeyToUser(String username) {
|
public User addApiKeyToUser(String username) {
|
||||||
User user =
|
User user =
|
||||||
userRepository
|
userRepository
|
||||||
.findByUsername(username)
|
.findByUsername(username)
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
|
|
||||||
user.setApiKey(generateApiKey());
|
user.setApiKey(generateApiKey());
|
||||||
return userRepository.save(user);
|
return userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public User refreshApiKeyForUser(String username) {
|
public User refreshApiKeyForUser(String username) {
|
||||||
return addApiKeyToUser(username); // reuse the add API key method for refreshing
|
return addApiKeyToUser(username); // reuse the add API key method for refreshing
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getApiKeyForUser(String username) {
|
public String getApiKeyForUser(String username) {
|
||||||
User user =
|
User user =
|
||||||
userRepository
|
userRepository
|
||||||
.findByUsername(username)
|
.findByUsername(username)
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
return user.getApiKey();
|
return user.getApiKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValidApiKey(String apiKey) {
|
public boolean isValidApiKey(String apiKey) {
|
||||||
return userRepository.findByApiKey(apiKey) != null;
|
return userRepository.findByApiKey(apiKey) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getUserByApiKey(String apiKey) {
|
public User getUserByApiKey(String apiKey) {
|
||||||
return userRepository.findByApiKey(apiKey);
|
return userRepository.findByApiKey(apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserDetails loadUserByApiKey(String apiKey) {
|
public UserDetails loadUserByApiKey(String apiKey) {
|
||||||
User userOptional = userRepository.findByApiKey(apiKey);
|
User userOptional = userRepository.findByApiKey(apiKey);
|
||||||
if (userOptional != null) {
|
if (userOptional != null) {
|
||||||
User user = userOptional;
|
User user = userOptional;
|
||||||
// Convert your User entity to a UserDetails object with authorities
|
// Convert your User entity to a UserDetails object with authorities
|
||||||
return new org.springframework.security.core.userdetails.User(
|
return new org.springframework.security.core.userdetails.User(
|
||||||
user.getUsername(),
|
user.getUsername(),
|
||||||
user.getPassword(), // you might not need this for API key auth
|
user.getPassword(), // you might not need this for API key auth
|
||||||
getAuthorities(user));
|
getAuthorities(user));
|
||||||
}
|
}
|
||||||
return null; // or throw an exception
|
return null; // or throw an exception
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateApiKeyForUser(String username, String apiKey) {
|
public boolean validateApiKeyForUser(String username, String apiKey) {
|
||||||
Optional<User> userOpt = userRepository.findByUsername(username);
|
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||||
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey);
|
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveUser(String username, String password) {
|
public void saveUser(String username, String password) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
user.setPassword(passwordEncoder.encode(password));
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveUser(String username, String password, String role, boolean firstLogin) {
|
public void saveUser(String username, String password, String role, boolean firstLogin) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
user.setPassword(passwordEncoder.encode(password));
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
user.addAuthority(new Authority(role, user));
|
user.addAuthority(new Authority(role, user));
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
user.setFirstLogin(firstLogin);
|
user.setFirstLogin(firstLogin);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveUser(String username, String password, String role) {
|
public void saveUser(String username, String password, String role) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
user.setPassword(passwordEncoder.encode(password));
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
user.addAuthority(new Authority(role, user));
|
user.addAuthority(new Authority(role, user));
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
user.setFirstLogin(false);
|
user.setFirstLogin(false);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteUser(String username) {
|
public void deleteUser(String username) {
|
||||||
Optional<User> userOpt = userRepository.findByUsername(username);
|
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
for (Authority authority : userOpt.get().getAuthorities()) {
|
for (Authority authority : userOpt.get().getAuthorities()) {
|
||||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userRepository.delete(userOpt.get());
|
userRepository.delete(userOpt.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean usernameExists(String username) {
|
public boolean usernameExists(String username) {
|
||||||
return userRepository.findByUsername(username).isPresent();
|
return userRepository.findByUsername(username).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasUsers() {
|
public boolean hasUsers() {
|
||||||
return userRepository.count() > 0;
|
return userRepository.count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateUserSettings(String username, Map<String, String> updates) {
|
public void updateUserSettings(String username, Map<String, String> updates) {
|
||||||
Optional<User> userOpt = userRepository.findByUsername(username);
|
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
Map<String, String> settingsMap = user.getSettings();
|
Map<String, String> settingsMap = user.getSettings();
|
||||||
|
|
||||||
if (settingsMap == null) {
|
if (settingsMap == null) {
|
||||||
settingsMap = new HashMap<String, String>();
|
settingsMap = new HashMap<String, String>();
|
||||||
}
|
}
|
||||||
settingsMap.clear();
|
settingsMap.clear();
|
||||||
settingsMap.putAll(updates);
|
settingsMap.putAll(updates);
|
||||||
user.setSettings(settingsMap);
|
user.setSettings(settingsMap);
|
||||||
|
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<User> findByUsername(String username) {
|
public Optional<User> findByUsername(String username) {
|
||||||
return userRepository.findByUsername(username);
|
return userRepository.findByUsername(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changeUsername(User user, String newUsername) {
|
public void changeUsername(User user, String newUsername) {
|
||||||
user.setUsername(newUsername);
|
user.setUsername(newUsername);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changePassword(User user, String newPassword) {
|
public void changePassword(User user, String newPassword) {
|
||||||
user.setPassword(passwordEncoder.encode(newPassword));
|
user.setPassword(passwordEncoder.encode(newPassword));
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changeFirstUse(User user, boolean firstUse) {
|
public void changeFirstUse(User user, boolean firstUse) {
|
||||||
user.setFirstLogin(firstUse);
|
user.setFirstLogin(firstUse);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPasswordCorrect(User user, String currentPassword) {
|
public boolean isPasswordCorrect(User user, String currentPassword) {
|
||||||
return passwordEncoder.matches(currentPassword, user.getPassword());
|
return passwordEncoder.matches(currentPassword, user.getPassword());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,11 +119,10 @@ public class SplitPdfBySectionsController {
|
||||||
// Set clipping area and position
|
// Set clipping area and position
|
||||||
float translateX = -subPageWidth * i;
|
float translateX = -subPageWidth * i;
|
||||||
float translateY = height - subPageHeight * (verticalDivisions - j);
|
float translateY = height - subPageHeight * (verticalDivisions - j);
|
||||||
|
|
||||||
|
// Code for google Docs pdfs..
|
||||||
//Code for google Docs pdfs..
|
// float translateY = -subPageHeight * (verticalDivisions - 1 - j);
|
||||||
//float translateY = -subPageHeight * (verticalDivisions - 1 - j);
|
|
||||||
|
|
||||||
contentStream.saveGraphicsState();
|
contentStream.saveGraphicsState();
|
||||||
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
||||||
contentStream.clip();
|
contentStream.clip();
|
||||||
|
|
|
@ -1,77 +1,77 @@
|
||||||
package stirling.software.SPDF.controller.api.converters;
|
package stirling.software.SPDF.controller.api.converters;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
|
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@Tag(name = "Convert", description = "Convert APIs")
|
@Tag(name = "Convert", description = "Convert APIs")
|
||||||
@RequestMapping("/api/v1/convert")
|
@RequestMapping("/api/v1/convert")
|
||||||
public class ConvertWebsiteToPDF {
|
public class ConvertWebsiteToPDF {
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert a URL to a PDF",
|
summary = "Convert a URL to a PDF",
|
||||||
description =
|
description =
|
||||||
"This endpoint fetches content from a URL and converts it to a PDF format. Input:N/A Output:PDF Type:SISO")
|
"This endpoint fetches content from a URL and converts it to a PDF format. Input:N/A Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> urlToPdf(@ModelAttribute UrlToPdfRequest request)
|
public ResponseEntity<byte[]> urlToPdf(@ModelAttribute UrlToPdfRequest request)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
String URL = request.getUrlInput();
|
String URL = request.getUrlInput();
|
||||||
|
|
||||||
// Validate the URL format
|
// Validate the URL format
|
||||||
if (!URL.matches("^https?://.*") || !GeneralUtils.isValidURL(URL)) {
|
if (!URL.matches("^https?://.*") || !GeneralUtils.isValidURL(URL)) {
|
||||||
throw new IllegalArgumentException("Invalid URL format provided.");
|
throw new IllegalArgumentException("Invalid URL format provided.");
|
||||||
}
|
}
|
||||||
Path tempOutputFile = null;
|
Path tempOutputFile = null;
|
||||||
byte[] pdfBytes;
|
byte[] pdfBytes;
|
||||||
try {
|
try {
|
||||||
// Prepare the output file path
|
// Prepare the output file path
|
||||||
tempOutputFile = Files.createTempFile("output_", ".pdf");
|
tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||||
|
|
||||||
// Prepare the OCRmyPDF command
|
// Prepare the OCRmyPDF command
|
||||||
List<String> command = new ArrayList<>();
|
List<String> command = new ArrayList<>();
|
||||||
command.add("weasyprint");
|
command.add("weasyprint");
|
||||||
command.add(URL);
|
command.add(URL);
|
||||||
command.add(tempOutputFile.toString());
|
command.add(tempOutputFile.toString());
|
||||||
|
|
||||||
ProcessExecutorResult returnCode =
|
ProcessExecutorResult returnCode =
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
|
||||||
.runCommandWithOutputHandling(command);
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
// Read the optimized PDF file
|
// Read the optimized PDF file
|
||||||
pdfBytes = Files.readAllBytes(tempOutputFile);
|
pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||||
} finally {
|
} finally {
|
||||||
// Clean up the temporary files
|
// Clean up the temporary files
|
||||||
Files.delete(tempOutputFile);
|
Files.delete(tempOutputFile);
|
||||||
}
|
}
|
||||||
// Convert URL to a safe filename
|
// Convert URL to a safe filename
|
||||||
String outputFilename = convertURLToFileName(URL);
|
String outputFilename = convertURLToFileName(URL);
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String convertURLToFileName(String url) {
|
private String convertURLToFileName(String url) {
|
||||||
String safeName = url.replaceAll("[^a-zA-Z0-9]", "_");
|
String safeName = url.replaceAll("[^a-zA-Z0-9]", "_");
|
||||||
if (safeName.length() > 50) {
|
if (safeName.length() > 50) {
|
||||||
safeName = safeName.substring(0, 50); // restrict to 50 characters
|
safeName = safeName.substring(0, 50); // restrict to 50 characters
|
||||||
}
|
}
|
||||||
return safeName + ".pdf";
|
return safeName + ".pdf";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,210 +1,210 @@
|
||||||
package stirling.software.SPDF.controller.api.filters;
|
package stirling.software.SPDF.controller.api.filters;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
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.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.PDFComparisonAndCount;
|
import stirling.software.SPDF.model.api.PDFComparisonAndCount;
|
||||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||||
import stirling.software.SPDF.model.api.filter.ContainsTextRequest;
|
import stirling.software.SPDF.model.api.filter.ContainsTextRequest;
|
||||||
import stirling.software.SPDF.model.api.filter.FileSizeRequest;
|
import stirling.software.SPDF.model.api.filter.FileSizeRequest;
|
||||||
import stirling.software.SPDF.model.api.filter.PageRotationRequest;
|
import stirling.software.SPDF.model.api.filter.PageRotationRequest;
|
||||||
import stirling.software.SPDF.model.api.filter.PageSizeRequest;
|
import stirling.software.SPDF.model.api.filter.PageSizeRequest;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/filter")
|
@RequestMapping("/api/v1/filter")
|
||||||
@Tag(name = "Filter", description = "Filter APIs")
|
@Tag(name = "Filter", description = "Filter APIs")
|
||||||
public class FilterController {
|
public class FilterController {
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/filter-contains-text")
|
@PostMapping(consumes = "multipart/form-data", value = "/filter-contains-text")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Checks if a PDF contains set text, returns true if does",
|
summary = "Checks if a PDF contains set text, returns true if does",
|
||||||
description = "Input:PDF Output:Boolean Type:SISO")
|
description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
public ResponseEntity<byte[]> containsText(@ModelAttribute ContainsTextRequest request)
|
public ResponseEntity<byte[]> containsText(@ModelAttribute ContainsTextRequest request)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String text = request.getText();
|
String text = request.getText();
|
||||||
String pageNumber = request.getPageNumbers();
|
String pageNumber = request.getPageNumbers();
|
||||||
|
|
||||||
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
||||||
if (PdfUtils.hasText(pdfDocument, pageNumber, text))
|
if (PdfUtils.hasText(pdfDocument, pageNumber, text))
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
pdfDocument, inputFile.getOriginalFilename());
|
pdfDocument, inputFile.getOriginalFilename());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/filter-contains-image")
|
@PostMapping(consumes = "multipart/form-data", value = "/filter-contains-image")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Checks if a PDF contains an image",
|
summary = "Checks if a PDF contains an image",
|
||||||
description = "Input:PDF Output:Boolean Type:SISO")
|
description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
public ResponseEntity<byte[]> containsImage(@ModelAttribute PDFWithPageNums request)
|
public ResponseEntity<byte[]> containsImage(@ModelAttribute PDFWithPageNums request)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String pageNumber = request.getPageNumbers();
|
String pageNumber = request.getPageNumbers();
|
||||||
|
|
||||||
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
||||||
if (PdfUtils.hasImages(pdfDocument, pageNumber))
|
if (PdfUtils.hasImages(pdfDocument, pageNumber))
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
pdfDocument, inputFile.getOriginalFilename());
|
pdfDocument, inputFile.getOriginalFilename());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-count")
|
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-count")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Checks if a PDF is greater, less or equal to a setPageCount",
|
summary = "Checks if a PDF is greater, less or equal to a setPageCount",
|
||||||
description = "Input:PDF Output:Boolean Type:SISO")
|
description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
public ResponseEntity<byte[]> pageCount(@ModelAttribute PDFComparisonAndCount request)
|
public ResponseEntity<byte[]> pageCount(@ModelAttribute PDFComparisonAndCount request)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String pageCount = request.getPageCount();
|
String pageCount = request.getPageCount();
|
||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
||||||
int actualPageCount = document.getNumberOfPages();
|
int actualPageCount = document.getNumberOfPages();
|
||||||
|
|
||||||
boolean valid = false;
|
boolean valid = false;
|
||||||
// Perform the comparison
|
// Perform the comparison
|
||||||
switch (comparator) {
|
switch (comparator) {
|
||||||
case "Greater":
|
case "Greater":
|
||||||
valid = actualPageCount > Integer.parseInt(pageCount);
|
valid = actualPageCount > Integer.parseInt(pageCount);
|
||||||
break;
|
break;
|
||||||
case "Equal":
|
case "Equal":
|
||||||
valid = actualPageCount == Integer.parseInt(pageCount);
|
valid = actualPageCount == Integer.parseInt(pageCount);
|
||||||
break;
|
break;
|
||||||
case "Less":
|
case "Less":
|
||||||
valid = actualPageCount < Integer.parseInt(pageCount);
|
valid = actualPageCount < Integer.parseInt(pageCount);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-size")
|
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-size")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Checks if a PDF is of a certain size",
|
summary = "Checks if a PDF is of a certain size",
|
||||||
description = "Input:PDF Output:Boolean Type:SISO")
|
description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
public ResponseEntity<byte[]> pageSize(@ModelAttribute PageSizeRequest request)
|
public ResponseEntity<byte[]> pageSize(@ModelAttribute PageSizeRequest request)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String standardPageSize = request.getStandardPageSize();
|
String standardPageSize = request.getStandardPageSize();
|
||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
|
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
||||||
|
|
||||||
PDPage firstPage = document.getPage(0);
|
PDPage firstPage = document.getPage(0);
|
||||||
PDRectangle actualPageSize = firstPage.getMediaBox();
|
PDRectangle actualPageSize = firstPage.getMediaBox();
|
||||||
|
|
||||||
// Calculate the area of the actual page size
|
// Calculate the area of the actual page size
|
||||||
float actualArea = actualPageSize.getWidth() * actualPageSize.getHeight();
|
float actualArea = actualPageSize.getWidth() * actualPageSize.getHeight();
|
||||||
|
|
||||||
// Get the standard size and calculate its area
|
// Get the standard size and calculate its area
|
||||||
PDRectangle standardSize = PdfUtils.textToPageSize(standardPageSize);
|
PDRectangle standardSize = PdfUtils.textToPageSize(standardPageSize);
|
||||||
float standardArea = standardSize.getWidth() * standardSize.getHeight();
|
float standardArea = standardSize.getWidth() * standardSize.getHeight();
|
||||||
|
|
||||||
boolean valid = false;
|
boolean valid = false;
|
||||||
// Perform the comparison
|
// Perform the comparison
|
||||||
switch (comparator) {
|
switch (comparator) {
|
||||||
case "Greater":
|
case "Greater":
|
||||||
valid = actualArea > standardArea;
|
valid = actualArea > standardArea;
|
||||||
break;
|
break;
|
||||||
case "Equal":
|
case "Equal":
|
||||||
valid = actualArea == standardArea;
|
valid = actualArea == standardArea;
|
||||||
break;
|
break;
|
||||||
case "Less":
|
case "Less":
|
||||||
valid = actualArea < standardArea;
|
valid = actualArea < standardArea;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/filter-file-size")
|
@PostMapping(consumes = "multipart/form-data", value = "/filter-file-size")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Checks if a PDF is a set file size",
|
summary = "Checks if a PDF is a set file size",
|
||||||
description = "Input:PDF Output:Boolean Type:SISO")
|
description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
public ResponseEntity<byte[]> fileSize(@ModelAttribute FileSizeRequest request)
|
public ResponseEntity<byte[]> fileSize(@ModelAttribute FileSizeRequest request)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String fileSize = request.getFileSize();
|
String fileSize = request.getFileSize();
|
||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
|
|
||||||
// Get the file size
|
// Get the file size
|
||||||
long actualFileSize = inputFile.getSize();
|
long actualFileSize = inputFile.getSize();
|
||||||
|
|
||||||
boolean valid = false;
|
boolean valid = false;
|
||||||
// Perform the comparison
|
// Perform the comparison
|
||||||
switch (comparator) {
|
switch (comparator) {
|
||||||
case "Greater":
|
case "Greater":
|
||||||
valid = actualFileSize > Long.parseLong(fileSize);
|
valid = actualFileSize > Long.parseLong(fileSize);
|
||||||
break;
|
break;
|
||||||
case "Equal":
|
case "Equal":
|
||||||
valid = actualFileSize == Long.parseLong(fileSize);
|
valid = actualFileSize == Long.parseLong(fileSize);
|
||||||
break;
|
break;
|
||||||
case "Less":
|
case "Less":
|
||||||
valid = actualFileSize < Long.parseLong(fileSize);
|
valid = actualFileSize < Long.parseLong(fileSize);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-rotation")
|
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-rotation")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Checks if a PDF is of a certain rotation",
|
summary = "Checks if a PDF is of a certain rotation",
|
||||||
description = "Input:PDF Output:Boolean Type:SISO")
|
description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
public ResponseEntity<byte[]> pageRotation(@ModelAttribute PageRotationRequest request)
|
public ResponseEntity<byte[]> pageRotation(@ModelAttribute PageRotationRequest request)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
int rotation = request.getRotation();
|
int rotation = request.getRotation();
|
||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
|
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
||||||
|
|
||||||
// Get the rotation of the first page
|
// Get the rotation of the first page
|
||||||
PDPage firstPage = document.getPage(0);
|
PDPage firstPage = document.getPage(0);
|
||||||
int actualRotation = firstPage.getRotation();
|
int actualRotation = firstPage.getRotation();
|
||||||
boolean valid = false;
|
boolean valid = false;
|
||||||
// Perform the comparison
|
// Perform the comparison
|
||||||
switch (comparator) {
|
switch (comparator) {
|
||||||
case "Greater":
|
case "Greater":
|
||||||
valid = actualRotation > rotation;
|
valid = actualRotation > rotation;
|
||||||
break;
|
break;
|
||||||
case "Equal":
|
case "Equal":
|
||||||
valid = actualRotation == rotation;
|
valid = actualRotation == rotation;
|
||||||
break;
|
break;
|
||||||
case "Less":
|
case "Less":
|
||||||
valid = actualRotation < rotation;
|
valid = actualRotation < rotation;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,150 +1,150 @@
|
||||||
package stirling.software.SPDF.controller.api.misc;
|
package stirling.software.SPDF.controller.api.misc;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
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.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.misc.AddPageNumbersRequest;
|
import stirling.software.SPDF.model.api.misc.AddPageNumbersRequest;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/misc")
|
@RequestMapping("/api/v1/misc")
|
||||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||||
public class PageNumbersController {
|
public class PageNumbersController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class);
|
private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class);
|
||||||
|
|
||||||
@PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data")
|
@PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Add page numbers to a PDF document",
|
summary = "Add page numbers to a PDF document",
|
||||||
description =
|
description =
|
||||||
"This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO")
|
"This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> addPageNumbers(@ModelAttribute AddPageNumbersRequest request)
|
public ResponseEntity<byte[]> addPageNumbers(@ModelAttribute AddPageNumbersRequest request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
String customMargin = request.getCustomMargin();
|
String customMargin = request.getCustomMargin();
|
||||||
int position = request.getPosition();
|
int position = request.getPosition();
|
||||||
int startingNumber = request.getStartingNumber();
|
int startingNumber = request.getStartingNumber();
|
||||||
String pagesToNumber = request.getPagesToNumber();
|
String pagesToNumber = request.getPagesToNumber();
|
||||||
String customText = request.getCustomText();
|
String customText = request.getCustomText();
|
||||||
int pageNumber = startingNumber;
|
int pageNumber = startingNumber;
|
||||||
byte[] fileBytes = file.getBytes();
|
byte[] fileBytes = file.getBytes();
|
||||||
PDDocument document = PDDocument.load(fileBytes);
|
PDDocument document = PDDocument.load(fileBytes);
|
||||||
|
|
||||||
float marginFactor;
|
float marginFactor;
|
||||||
switch (customMargin.toLowerCase()) {
|
switch (customMargin.toLowerCase()) {
|
||||||
case "small":
|
case "small":
|
||||||
marginFactor = 0.02f;
|
marginFactor = 0.02f;
|
||||||
break;
|
break;
|
||||||
case "medium":
|
case "medium":
|
||||||
marginFactor = 0.035f;
|
marginFactor = 0.035f;
|
||||||
break;
|
break;
|
||||||
case "large":
|
case "large":
|
||||||
marginFactor = 0.05f;
|
marginFactor = 0.05f;
|
||||||
break;
|
break;
|
||||||
case "x-large":
|
case "x-large":
|
||||||
marginFactor = 0.075f;
|
marginFactor = 0.075f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
marginFactor = 0.035f;
|
marginFactor = 0.035f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
float fontSize = 12.0f;
|
float fontSize = 12.0f;
|
||||||
PDType1Font font = PDType1Font.HELVETICA;
|
PDType1Font font = PDType1Font.HELVETICA;
|
||||||
if (pagesToNumber == null || pagesToNumber.length() == 0) {
|
if (pagesToNumber == null || pagesToNumber.length() == 0) {
|
||||||
pagesToNumber = "all";
|
pagesToNumber = "all";
|
||||||
}
|
}
|
||||||
if (customText == null || customText.length() == 0) {
|
if (customText == null || customText.length() == 0) {
|
||||||
customText = "{n}";
|
customText = "{n}";
|
||||||
}
|
}
|
||||||
List<Integer> pagesToNumberList =
|
List<Integer> pagesToNumberList =
|
||||||
GeneralUtils.parsePageList(pagesToNumber.split(","), document.getNumberOfPages());
|
GeneralUtils.parsePageList(pagesToNumber.split(","), document.getNumberOfPages());
|
||||||
|
|
||||||
for (int i : pagesToNumberList) {
|
for (int i : pagesToNumberList) {
|
||||||
PDPage page = document.getPage(i);
|
PDPage page = document.getPage(i);
|
||||||
PDRectangle pageSize = page.getMediaBox();
|
PDRectangle pageSize = page.getMediaBox();
|
||||||
|
|
||||||
String text =
|
String text =
|
||||||
customText != null
|
customText != null
|
||||||
? customText
|
? customText
|
||||||
.replace("{n}", String.valueOf(pageNumber))
|
.replace("{n}", String.valueOf(pageNumber))
|
||||||
.replace("{total}", String.valueOf(document.getNumberOfPages()))
|
.replace("{total}", String.valueOf(document.getNumberOfPages()))
|
||||||
.replace(
|
.replace(
|
||||||
"{filename}",
|
"{filename}",
|
||||||
file.getOriginalFilename()
|
file.getOriginalFilename()
|
||||||
.replaceFirst("[.][^.]+$", ""))
|
.replaceFirst("[.][^.]+$", ""))
|
||||||
: String.valueOf(pageNumber);
|
: String.valueOf(pageNumber);
|
||||||
|
|
||||||
float x, y;
|
float x, y;
|
||||||
|
|
||||||
int xGroup = (position - 1) % 3;
|
int xGroup = (position - 1) % 3;
|
||||||
int yGroup = 2 - (position - 1) / 3;
|
int yGroup = 2 - (position - 1) / 3;
|
||||||
|
|
||||||
switch (xGroup) {
|
switch (xGroup) {
|
||||||
case 0: // left
|
case 0: // left
|
||||||
x = pageSize.getLowerLeftX() + marginFactor * pageSize.getWidth();
|
x = pageSize.getLowerLeftX() + marginFactor * pageSize.getWidth();
|
||||||
break;
|
break;
|
||||||
case 1: // center
|
case 1: // center
|
||||||
x = pageSize.getLowerLeftX() + (pageSize.getWidth() / 2);
|
x = pageSize.getLowerLeftX() + (pageSize.getWidth() / 2);
|
||||||
break;
|
break;
|
||||||
default: // right
|
default: // right
|
||||||
x = pageSize.getUpperRightX() - marginFactor * pageSize.getWidth();
|
x = pageSize.getUpperRightX() - marginFactor * pageSize.getWidth();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (yGroup) {
|
switch (yGroup) {
|
||||||
case 0: // bottom
|
case 0: // bottom
|
||||||
y = pageSize.getLowerLeftY() + marginFactor * pageSize.getHeight();
|
y = pageSize.getLowerLeftY() + marginFactor * pageSize.getHeight();
|
||||||
break;
|
break;
|
||||||
case 1: // middle
|
case 1: // middle
|
||||||
y = pageSize.getLowerLeftY() + (pageSize.getHeight() / 2);
|
y = pageSize.getLowerLeftY() + (pageSize.getHeight() / 2);
|
||||||
break;
|
break;
|
||||||
default: // top
|
default: // top
|
||||||
y = pageSize.getUpperRightY() - marginFactor * pageSize.getHeight();
|
y = pageSize.getUpperRightY() - marginFactor * pageSize.getHeight();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
PDPageContentStream contentStream =
|
PDPageContentStream contentStream =
|
||||||
new PDPageContentStream(
|
new PDPageContentStream(
|
||||||
document, page, PDPageContentStream.AppendMode.APPEND, true);
|
document, page, PDPageContentStream.AppendMode.APPEND, true);
|
||||||
contentStream.beginText();
|
contentStream.beginText();
|
||||||
contentStream.setFont(font, fontSize);
|
contentStream.setFont(font, fontSize);
|
||||||
contentStream.newLineAtOffset(x, y);
|
contentStream.newLineAtOffset(x, y);
|
||||||
contentStream.showText(text);
|
contentStream.showText(text);
|
||||||
contentStream.endText();
|
contentStream.endText();
|
||||||
contentStream.close();
|
contentStream.close();
|
||||||
|
|
||||||
pageNumber++;
|
pageNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
document.save(baos);
|
document.save(baos);
|
||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
baos.toByteArray(),
|
baos.toByteArray(),
|
||||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf",
|
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf",
|
||||||
MediaType.APPLICATION_PDF);
|
MediaType.APPLICATION_PDF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,133 +1,133 @@
|
||||||
package stirling.software.SPDF.controller.api.pipeline;
|
package stirling.software.SPDF.controller.api.pipeline;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
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.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
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 com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.PipelineConfig;
|
import stirling.software.SPDF.model.PipelineConfig;
|
||||||
import stirling.software.SPDF.model.api.HandleDataRequest;
|
import stirling.software.SPDF.model.api.HandleDataRequest;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/pipeline")
|
@RequestMapping("/api/v1/pipeline")
|
||||||
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
||||||
public class PipelineController {
|
public class PipelineController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PipelineController.class);
|
private static final Logger logger = LoggerFactory.getLogger(PipelineController.class);
|
||||||
|
|
||||||
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
||||||
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
||||||
@Autowired PipelineProcessor processor;
|
@Autowired PipelineProcessor processor;
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Autowired private ObjectMapper objectMapper;
|
@Autowired private ObjectMapper objectMapper;
|
||||||
|
|
||||||
@PostMapping("/handleData")
|
@PostMapping("/handleData")
|
||||||
public ResponseEntity<byte[]> handleData(@ModelAttribute HandleDataRequest request)
|
public ResponseEntity<byte[]> handleData(@ModelAttribute HandleDataRequest request)
|
||||||
throws JsonMappingException, JsonProcessingException {
|
throws JsonMappingException, JsonProcessingException {
|
||||||
if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) {
|
if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) {
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
MultipartFile[] files = request.getFileInput();
|
MultipartFile[] files = request.getFileInput();
|
||||||
String jsonString = request.getJson();
|
String jsonString = request.getJson();
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
PipelineConfig config = objectMapper.readValue(jsonString, PipelineConfig.class);
|
PipelineConfig config = objectMapper.readValue(jsonString, PipelineConfig.class);
|
||||||
logger.info("Received POST request to /handleData with {} files", files.length);
|
logger.info("Received POST request to /handleData with {} files", files.length);
|
||||||
try {
|
try {
|
||||||
List<Resource> inputFiles = processor.generateInputFiles(files);
|
List<Resource> inputFiles = processor.generateInputFiles(files);
|
||||||
if (inputFiles == null || inputFiles.size() == 0) {
|
if (inputFiles == null || inputFiles.size() == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<Resource> outputFiles = processor.runPipelineAgainstFiles(inputFiles, config);
|
List<Resource> outputFiles = processor.runPipelineAgainstFiles(inputFiles, config);
|
||||||
if (outputFiles != null && outputFiles.size() == 1) {
|
if (outputFiles != null && outputFiles.size() == 1) {
|
||||||
// If there is only one file, return it directly
|
// If there is only one file, return it directly
|
||||||
Resource singleFile = outputFiles.get(0);
|
Resource singleFile = outputFiles.get(0);
|
||||||
InputStream is = singleFile.getInputStream();
|
InputStream is = singleFile.getInputStream();
|
||||||
byte[] bytes = new byte[(int) singleFile.contentLength()];
|
byte[] bytes = new byte[(int) singleFile.contentLength()];
|
||||||
is.read(bytes);
|
is.read(bytes);
|
||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
logger.info("Returning single file response...");
|
logger.info("Returning single file response...");
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
bytes, singleFile.getFilename(), MediaType.APPLICATION_OCTET_STREAM);
|
bytes, singleFile.getFilename(), MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} else if (outputFiles == null) {
|
} else if (outputFiles == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a ByteArrayOutputStream to hold the zip
|
// Create a ByteArrayOutputStream to hold the zip
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ZipOutputStream zipOut = new ZipOutputStream(baos);
|
ZipOutputStream zipOut = new ZipOutputStream(baos);
|
||||||
|
|
||||||
// A map to keep track of filenames and their counts
|
// A map to keep track of filenames and their counts
|
||||||
Map<String, Integer> filenameCount = new HashMap<>();
|
Map<String, Integer> filenameCount = new HashMap<>();
|
||||||
|
|
||||||
// Loop through each file and add it to the zip
|
// Loop through each file and add it to the zip
|
||||||
for (Resource file : outputFiles) {
|
for (Resource file : outputFiles) {
|
||||||
String originalFilename = file.getFilename();
|
String originalFilename = file.getFilename();
|
||||||
String filename = originalFilename;
|
String filename = originalFilename;
|
||||||
|
|
||||||
// Check if the filename already exists, and modify it if necessary
|
// Check if the filename already exists, and modify it if necessary
|
||||||
if (filenameCount.containsKey(originalFilename)) {
|
if (filenameCount.containsKey(originalFilename)) {
|
||||||
int count = filenameCount.get(originalFilename);
|
int count = filenameCount.get(originalFilename);
|
||||||
String baseName = originalFilename.replaceAll("\\.[^.]*$", "");
|
String baseName = originalFilename.replaceAll("\\.[^.]*$", "");
|
||||||
String extension = originalFilename.replaceAll("^.*\\.", "");
|
String extension = originalFilename.replaceAll("^.*\\.", "");
|
||||||
filename = baseName + "(" + count + ")." + extension;
|
filename = baseName + "(" + count + ")." + extension;
|
||||||
filenameCount.put(originalFilename, count + 1);
|
filenameCount.put(originalFilename, count + 1);
|
||||||
} else {
|
} else {
|
||||||
filenameCount.put(originalFilename, 1);
|
filenameCount.put(originalFilename, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZipEntry zipEntry = new ZipEntry(filename);
|
ZipEntry zipEntry = new ZipEntry(filename);
|
||||||
zipOut.putNextEntry(zipEntry);
|
zipOut.putNextEntry(zipEntry);
|
||||||
|
|
||||||
// Read the file into a byte array
|
// Read the file into a byte array
|
||||||
InputStream is = file.getInputStream();
|
InputStream is = file.getInputStream();
|
||||||
byte[] bytes = new byte[(int) file.contentLength()];
|
byte[] bytes = new byte[(int) file.contentLength()];
|
||||||
is.read(bytes);
|
is.read(bytes);
|
||||||
|
|
||||||
// Write the bytes of the file to the zip
|
// Write the bytes of the file to the zip
|
||||||
zipOut.write(bytes, 0, bytes.length);
|
zipOut.write(bytes, 0, bytes.length);
|
||||||
zipOut.closeEntry();
|
zipOut.closeEntry();
|
||||||
|
|
||||||
is.close();
|
is.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
|
|
||||||
logger.info("Returning zipped file response...");
|
logger.info("Returning zipped file response...");
|
||||||
return WebResponseUtils.boasToWebResponse(
|
return WebResponseUtils.boasToWebResponse(
|
||||||
baos, "output.zip", MediaType.APPLICATION_OCTET_STREAM);
|
baos, "output.zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error handling data: ", e);
|
logger.error("Error handling data: ", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,171 +1,171 @@
|
||||||
package stirling.software.SPDF.controller.api.security;
|
package stirling.software.SPDF.controller.api.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.pdfbox.cos.COSDictionary;
|
import org.apache.pdfbox.cos.COSDictionary;
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||||
import org.apache.pdfbox.pdmodel.PDResources;
|
import org.apache.pdfbox.pdmodel.PDResources;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDMetadata;
|
import org.apache.pdfbox.pdmodel.common.PDMetadata;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
|
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
|
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.action.PDActionLaunch;
|
import org.apache.pdfbox.pdmodel.interactive.action.PDActionLaunch;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
|
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
|
import org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
|
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
|
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
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.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.security.SanitizePdfRequest;
|
import stirling.software.SPDF.model.api.security.SanitizePdfRequest;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/security")
|
@RequestMapping("/api/v1/security")
|
||||||
@Tag(name = "Security", description = "Security APIs")
|
@Tag(name = "Security", description = "Security APIs")
|
||||||
public class SanitizeController {
|
public class SanitizeController {
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Sanitize a PDF file",
|
summary = "Sanitize a PDF file",
|
||||||
description =
|
description =
|
||||||
"This endpoint processes a PDF file and removes specific elements based on the provided options. Input:PDF Output:PDF Type:SISO")
|
"This endpoint processes a PDF file and removes specific elements based on the provided options. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> sanitizePDF(@ModelAttribute SanitizePdfRequest request)
|
public ResponseEntity<byte[]> sanitizePDF(@ModelAttribute SanitizePdfRequest request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
boolean removeJavaScript = request.isRemoveJavaScript();
|
boolean removeJavaScript = request.isRemoveJavaScript();
|
||||||
boolean removeEmbeddedFiles = request.isRemoveEmbeddedFiles();
|
boolean removeEmbeddedFiles = request.isRemoveEmbeddedFiles();
|
||||||
boolean removeMetadata = request.isRemoveMetadata();
|
boolean removeMetadata = request.isRemoveMetadata();
|
||||||
boolean removeLinks = request.isRemoveLinks();
|
boolean removeLinks = request.isRemoveLinks();
|
||||||
boolean removeFonts = request.isRemoveFonts();
|
boolean removeFonts = request.isRemoveFonts();
|
||||||
|
|
||||||
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) {
|
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) {
|
||||||
if (removeJavaScript) {
|
if (removeJavaScript) {
|
||||||
sanitizeJavaScript(document);
|
sanitizeJavaScript(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeEmbeddedFiles) {
|
if (removeEmbeddedFiles) {
|
||||||
sanitizeEmbeddedFiles(document);
|
sanitizeEmbeddedFiles(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeMetadata) {
|
if (removeMetadata) {
|
||||||
sanitizeMetadata(document);
|
sanitizeMetadata(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeLinks) {
|
if (removeLinks) {
|
||||||
sanitizeLinks(document);
|
sanitizeLinks(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeFonts) {
|
if (removeFonts) {
|
||||||
sanitizeFonts(document);
|
sanitizeFonts(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||||
+ "_sanitized.pdf");
|
+ "_sanitized.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeJavaScript(PDDocument document) throws IOException {
|
private void sanitizeJavaScript(PDDocument document) throws IOException {
|
||||||
// Get the root dictionary (catalog) of the PDF
|
// Get the root dictionary (catalog) of the PDF
|
||||||
PDDocumentCatalog catalog = document.getDocumentCatalog();
|
PDDocumentCatalog catalog = document.getDocumentCatalog();
|
||||||
|
|
||||||
// Get the Names dictionary
|
// Get the Names dictionary
|
||||||
COSDictionary namesDict =
|
COSDictionary namesDict =
|
||||||
(COSDictionary) catalog.getCOSObject().getDictionaryObject(COSName.NAMES);
|
(COSDictionary) catalog.getCOSObject().getDictionaryObject(COSName.NAMES);
|
||||||
|
|
||||||
if (namesDict != null) {
|
if (namesDict != null) {
|
||||||
// Get the JavaScript dictionary
|
// Get the JavaScript dictionary
|
||||||
COSDictionary javaScriptDict =
|
COSDictionary javaScriptDict =
|
||||||
(COSDictionary) namesDict.getDictionaryObject(COSName.getPDFName("JavaScript"));
|
(COSDictionary) namesDict.getDictionaryObject(COSName.getPDFName("JavaScript"));
|
||||||
|
|
||||||
if (javaScriptDict != null) {
|
if (javaScriptDict != null) {
|
||||||
// Remove the JavaScript dictionary
|
// Remove the JavaScript dictionary
|
||||||
namesDict.removeItem(COSName.getPDFName("JavaScript"));
|
namesDict.removeItem(COSName.getPDFName("JavaScript"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
for (PDAnnotation annotation : page.getAnnotations()) {
|
for (PDAnnotation annotation : page.getAnnotations()) {
|
||||||
if (annotation instanceof PDAnnotationWidget) {
|
if (annotation instanceof PDAnnotationWidget) {
|
||||||
PDAnnotationWidget widget = (PDAnnotationWidget) annotation;
|
PDAnnotationWidget widget = (PDAnnotationWidget) annotation;
|
||||||
PDAction action = widget.getAction();
|
PDAction action = widget.getAction();
|
||||||
if (action instanceof PDActionJavaScript) {
|
if (action instanceof PDActionJavaScript) {
|
||||||
widget.setAction(null);
|
widget.setAction(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
|
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
|
||||||
if (acroForm != null) {
|
if (acroForm != null) {
|
||||||
for (PDField field : acroForm.getFields()) {
|
for (PDField field : acroForm.getFields()) {
|
||||||
PDFormFieldAdditionalActions actions = field.getActions();
|
PDFormFieldAdditionalActions actions = field.getActions();
|
||||||
if (actions != null) {
|
if (actions != null) {
|
||||||
if (actions.getC() instanceof PDActionJavaScript) {
|
if (actions.getC() instanceof PDActionJavaScript) {
|
||||||
actions.setC(null);
|
actions.setC(null);
|
||||||
}
|
}
|
||||||
if (actions.getF() instanceof PDActionJavaScript) {
|
if (actions.getF() instanceof PDActionJavaScript) {
|
||||||
actions.setF(null);
|
actions.setF(null);
|
||||||
}
|
}
|
||||||
if (actions.getK() instanceof PDActionJavaScript) {
|
if (actions.getK() instanceof PDActionJavaScript) {
|
||||||
actions.setK(null);
|
actions.setK(null);
|
||||||
}
|
}
|
||||||
if (actions.getV() instanceof PDActionJavaScript) {
|
if (actions.getV() instanceof PDActionJavaScript) {
|
||||||
actions.setV(null);
|
actions.setV(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeEmbeddedFiles(PDDocument document) {
|
private void sanitizeEmbeddedFiles(PDDocument document) {
|
||||||
PDPageTree allPages = document.getPages();
|
PDPageTree allPages = document.getPages();
|
||||||
|
|
||||||
for (PDPage page : allPages) {
|
for (PDPage page : allPages) {
|
||||||
PDResources res = page.getResources();
|
PDResources res = page.getResources();
|
||||||
|
|
||||||
// Remove embedded files from the PDF
|
// Remove embedded files from the PDF
|
||||||
res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles"));
|
res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeMetadata(PDDocument document) {
|
private void sanitizeMetadata(PDDocument document) {
|
||||||
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
|
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
document.getDocumentCatalog().setMetadata(null);
|
document.getDocumentCatalog().setMetadata(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeLinks(PDDocument document) throws IOException {
|
private void sanitizeLinks(PDDocument document) throws IOException {
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
for (PDAnnotation annotation : page.getAnnotations()) {
|
for (PDAnnotation annotation : page.getAnnotations()) {
|
||||||
if (annotation instanceof PDAnnotationLink) {
|
if (annotation instanceof PDAnnotationLink) {
|
||||||
PDAction action = ((PDAnnotationLink) annotation).getAction();
|
PDAction action = ((PDAnnotationLink) annotation).getAction();
|
||||||
if (action instanceof PDActionLaunch || action instanceof PDActionURI) {
|
if (action instanceof PDActionLaunch || action instanceof PDActionURI) {
|
||||||
((PDAnnotationLink) annotation).setAction(null);
|
((PDAnnotationLink) annotation).setAction(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeFonts(PDDocument document) {
|
private void sanitizeFonts(PDDocument document) {
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font"));
|
page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,113 +1,113 @@
|
||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
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.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@Tag(name = "Convert", description = "Convert APIs")
|
@Tag(name = "Convert", description = "Convert APIs")
|
||||||
public class ConverterWebController {
|
public class ConverterWebController {
|
||||||
|
|
||||||
@GetMapping("/img-to-pdf")
|
@GetMapping("/img-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertImgToPdfForm(Model model) {
|
public String convertImgToPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "img-to-pdf");
|
model.addAttribute("currentPage", "img-to-pdf");
|
||||||
return "convert/img-to-pdf";
|
return "convert/img-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/html-to-pdf")
|
@GetMapping("/html-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertHTMLToPdfForm(Model model) {
|
public String convertHTMLToPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "html-to-pdf");
|
model.addAttribute("currentPage", "html-to-pdf");
|
||||||
return "convert/html-to-pdf";
|
return "convert/html-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/markdown-to-pdf")
|
@GetMapping("/markdown-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertMarkdownToPdfForm(Model model) {
|
public String convertMarkdownToPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "markdown-to-pdf");
|
model.addAttribute("currentPage", "markdown-to-pdf");
|
||||||
return "convert/markdown-to-pdf";
|
return "convert/markdown-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/url-to-pdf")
|
@GetMapping("/url-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertURLToPdfForm(Model model) {
|
public String convertURLToPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "url-to-pdf");
|
model.addAttribute("currentPage", "url-to-pdf");
|
||||||
return "convert/url-to-pdf";
|
return "convert/url-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-img")
|
@GetMapping("/pdf-to-img")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String pdfToimgForm(Model model) {
|
public String pdfToimgForm(Model model) {
|
||||||
model.addAttribute("currentPage", "pdf-to-img");
|
model.addAttribute("currentPage", "pdf-to-img");
|
||||||
return "convert/pdf-to-img";
|
return "convert/pdf-to-img";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/file-to-pdf")
|
@GetMapping("/file-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertToPdfForm(Model model) {
|
public String convertToPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "file-to-pdf");
|
model.addAttribute("currentPage", "file-to-pdf");
|
||||||
return "convert/file-to-pdf";
|
return "convert/file-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
// PDF TO......
|
// PDF TO......
|
||||||
|
|
||||||
@GetMapping("/pdf-to-html")
|
@GetMapping("/pdf-to-html")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView pdfToHTML() {
|
public ModelAndView pdfToHTML() {
|
||||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html");
|
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html");
|
||||||
modelAndView.addObject("currentPage", "pdf-to-html");
|
modelAndView.addObject("currentPage", "pdf-to-html");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-presentation")
|
@GetMapping("/pdf-to-presentation")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView pdfToPresentation() {
|
public ModelAndView pdfToPresentation() {
|
||||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation");
|
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation");
|
||||||
modelAndView.addObject("currentPage", "pdf-to-presentation");
|
modelAndView.addObject("currentPage", "pdf-to-presentation");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-text")
|
@GetMapping("/pdf-to-text")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView pdfToText() {
|
public ModelAndView pdfToText() {
|
||||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text");
|
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text");
|
||||||
modelAndView.addObject("currentPage", "pdf-to-text");
|
modelAndView.addObject("currentPage", "pdf-to-text");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-word")
|
@GetMapping("/pdf-to-word")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView pdfToWord() {
|
public ModelAndView pdfToWord() {
|
||||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word");
|
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word");
|
||||||
modelAndView.addObject("currentPage", "pdf-to-word");
|
modelAndView.addObject("currentPage", "pdf-to-word");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-xml")
|
@GetMapping("/pdf-to-xml")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView pdfToXML() {
|
public ModelAndView pdfToXML() {
|
||||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml");
|
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml");
|
||||||
modelAndView.addObject("currentPage", "pdf-to-xml");
|
modelAndView.addObject("currentPage", "pdf-to-xml");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-csv")
|
@GetMapping("/pdf-to-csv")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView pdfToCSV() {
|
public ModelAndView pdfToCSV() {
|
||||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-csv");
|
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-csv");
|
||||||
modelAndView.addObject("currentPage", "pdf-to-csv");
|
modelAndView.addObject("currentPage", "pdf-to-csv");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-pdfa")
|
@GetMapping("/pdf-to-pdfa")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String pdfToPdfAForm(Model model) {
|
public String pdfToPdfAForm(Model model) {
|
||||||
model.addAttribute("currentPage", "pdf-to-pdfa");
|
model.addAttribute("currentPage", "pdf-to-pdfa");
|
||||||
return "convert/pdf-to-pdfa";
|
return "convert/pdf-to-pdfa";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +1,69 @@
|
||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
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 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;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@Tag(name = "Security", description = "Security APIs")
|
@Tag(name = "Security", description = "Security APIs")
|
||||||
public class SecurityWebController {
|
public class SecurityWebController {
|
||||||
|
|
||||||
@GetMapping("/auto-redact")
|
@GetMapping("/auto-redact")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String autoRedactForm(Model model) {
|
public String autoRedactForm(Model model) {
|
||||||
model.addAttribute("currentPage", "auto-redact");
|
model.addAttribute("currentPage", "auto-redact");
|
||||||
return "security/auto-redact";
|
return "security/auto-redact";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/add-password")
|
@GetMapping("/add-password")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String addPasswordForm(Model model) {
|
public String addPasswordForm(Model model) {
|
||||||
model.addAttribute("currentPage", "add-password");
|
model.addAttribute("currentPage", "add-password");
|
||||||
return "security/add-password";
|
return "security/add-password";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/change-permissions")
|
@GetMapping("/change-permissions")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String permissionsForm(Model model) {
|
public String permissionsForm(Model model) {
|
||||||
model.addAttribute("currentPage", "change-permissions");
|
model.addAttribute("currentPage", "change-permissions");
|
||||||
return "security/change-permissions";
|
return "security/change-permissions";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/remove-password")
|
@GetMapping("/remove-password")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String removePasswordForm(Model model) {
|
public String removePasswordForm(Model model) {
|
||||||
model.addAttribute("currentPage", "remove-password");
|
model.addAttribute("currentPage", "remove-password");
|
||||||
return "security/remove-password";
|
return "security/remove-password";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/add-watermark")
|
@GetMapping("/add-watermark")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String addWatermarkForm(Model model) {
|
public String addWatermarkForm(Model model) {
|
||||||
model.addAttribute("currentPage", "add-watermark");
|
model.addAttribute("currentPage", "add-watermark");
|
||||||
return "security/add-watermark";
|
return "security/add-watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/cert-sign")
|
@GetMapping("/cert-sign")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String certSignForm(Model model) {
|
public String certSignForm(Model model) {
|
||||||
model.addAttribute("currentPage", "cert-sign");
|
model.addAttribute("currentPage", "cert-sign");
|
||||||
return "security/cert-sign";
|
return "security/cert-sign";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/sanitize-pdf")
|
@GetMapping("/sanitize-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String sanitizeForm(Model model) {
|
public String sanitizeForm(Model model) {
|
||||||
model.addAttribute("currentPage", "sanitize-pdf");
|
model.addAttribute("currentPage", "sanitize-pdf");
|
||||||
return "security/sanitize-pdf";
|
return "security/sanitize-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/get-info-on-pdf")
|
@GetMapping("/get-info-on-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String getInfo(Model model) {
|
public String getInfo(Model model) {
|
||||||
model.addAttribute("currentPage", "get-info-on-pdf");
|
model.addAttribute("currentPage", "get-info-on-pdf");
|
||||||
return "security/get-info-on-pdf";
|
return "security/get-info-on-pdf";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,186 +1,186 @@
|
||||||
package stirling.software.SPDF.utils;
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.FileVisitResult;
|
import java.nio.file.FileVisitResult;
|
||||||
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.nio.file.SimpleFileVisitor;
|
import java.nio.file.SimpleFileVisitor;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
public class GeneralUtils {
|
public class GeneralUtils {
|
||||||
|
|
||||||
public static void deleteDirectory(Path path) throws IOException {
|
public static void deleteDirectory(Path path) throws IOException {
|
||||||
Files.walkFileTree(
|
Files.walkFileTree(
|
||||||
path,
|
path,
|
||||||
new SimpleFileVisitor<Path>() {
|
new SimpleFileVisitor<Path>() {
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Files.delete(file);
|
Files.delete(file);
|
||||||
return FileVisitResult.CONTINUE;
|
return FileVisitResult.CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
|
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Files.delete(dir);
|
Files.delete(dir);
|
||||||
return FileVisitResult.CONTINUE;
|
return FileVisitResult.CONTINUE;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String convertToFileName(String name) {
|
public static String convertToFileName(String name) {
|
||||||
String safeName = name.replaceAll("[^a-zA-Z0-9]", "_");
|
String safeName = name.replaceAll("[^a-zA-Z0-9]", "_");
|
||||||
if (safeName.length() > 50) {
|
if (safeName.length() > 50) {
|
||||||
safeName = safeName.substring(0, 50);
|
safeName = safeName.substring(0, 50);
|
||||||
}
|
}
|
||||||
return safeName;
|
return safeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidURL(String urlStr) {
|
public static boolean isValidURL(String urlStr) {
|
||||||
try {
|
try {
|
||||||
new URL(urlStr);
|
new URL(urlStr);
|
||||||
return true;
|
return true;
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File multipartToFile(MultipartFile multipart) throws IOException {
|
public static File multipartToFile(MultipartFile multipart) throws IOException {
|
||||||
Path tempFile = Files.createTempFile("overlay-", ".pdf");
|
Path tempFile = Files.createTempFile("overlay-", ".pdf");
|
||||||
try (InputStream in = multipart.getInputStream();
|
try (InputStream in = multipart.getInputStream();
|
||||||
FileOutputStream out = new FileOutputStream(tempFile.toFile())) {
|
FileOutputStream out = new FileOutputStream(tempFile.toFile())) {
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
while ((bytesRead = in.read(buffer)) != -1) {
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
out.write(buffer, 0, bytesRead);
|
out.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tempFile.toFile();
|
return tempFile.toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Long convertSizeToBytes(String sizeStr) {
|
public static Long convertSizeToBytes(String sizeStr) {
|
||||||
if (sizeStr == null) {
|
if (sizeStr == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
sizeStr = sizeStr.trim().toUpperCase();
|
sizeStr = sizeStr.trim().toUpperCase();
|
||||||
try {
|
try {
|
||||||
if (sizeStr.endsWith("KB")) {
|
if (sizeStr.endsWith("KB")) {
|
||||||
return (long)
|
return (long)
|
||||||
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
|
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
|
||||||
} else if (sizeStr.endsWith("MB")) {
|
} else if (sizeStr.endsWith("MB")) {
|
||||||
return (long)
|
return (long)
|
||||||
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
||||||
* 1024
|
* 1024
|
||||||
* 1024);
|
* 1024);
|
||||||
} else if (sizeStr.endsWith("GB")) {
|
} else if (sizeStr.endsWith("GB")) {
|
||||||
return (long)
|
return (long)
|
||||||
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
||||||
* 1024
|
* 1024
|
||||||
* 1024
|
* 1024
|
||||||
* 1024);
|
* 1024);
|
||||||
} else if (sizeStr.endsWith("B")) {
|
} else if (sizeStr.endsWith("B")) {
|
||||||
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
||||||
} else {
|
} else {
|
||||||
// Assume MB if no unit is specified
|
// Assume MB if no unit is specified
|
||||||
return (long) (Double.parseDouble(sizeStr) * 1024 * 1024);
|
return (long) (Double.parseDouble(sizeStr) * 1024 * 1024);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
// The numeric part of the input string cannot be parsed, handle this case
|
// The numeric part of the input string cannot be parsed, handle this case
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> parsePageString(String pageOrder, int totalPages) {
|
public static List<Integer> parsePageString(String pageOrder, int totalPages) {
|
||||||
return parsePageList(pageOrder.split(","), totalPages);
|
return parsePageList(pageOrder.split(","), totalPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> parsePageList(String[] pageOrderArr, int totalPages) {
|
public static List<Integer> parsePageList(String[] pageOrderArr, int totalPages) {
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
|
|
||||||
// loop through the page order array
|
// loop through the page order array
|
||||||
for (String element : pageOrderArr) {
|
for (String element : pageOrderArr) {
|
||||||
if (element.equalsIgnoreCase("all")) {
|
if (element.equalsIgnoreCase("all")) {
|
||||||
for (int i = 0; i < totalPages; i++) {
|
for (int i = 0; i < totalPages; i++) {
|
||||||
newPageOrder.add(i);
|
newPageOrder.add(i);
|
||||||
}
|
}
|
||||||
// As all pages are already added, no need to check further
|
// As all pages are already added, no need to check further
|
||||||
break;
|
break;
|
||||||
} else if (element.matches("\\d*n\\+?-?\\d*|\\d*\\+?n")) {
|
} else if (element.matches("\\d*n\\+?-?\\d*|\\d*\\+?n")) {
|
||||||
// Handle page order as a function
|
// Handle page order as a function
|
||||||
int coefficient = 0;
|
int coefficient = 0;
|
||||||
int constant = 0;
|
int constant = 0;
|
||||||
boolean coefficientExists = false;
|
boolean coefficientExists = false;
|
||||||
boolean constantExists = false;
|
boolean constantExists = false;
|
||||||
|
|
||||||
if (element.contains("n")) {
|
if (element.contains("n")) {
|
||||||
String[] parts = element.split("n");
|
String[] parts = element.split("n");
|
||||||
if (!parts[0].equals("") && parts[0] != null) {
|
if (!parts[0].equals("") && parts[0] != null) {
|
||||||
coefficient = Integer.parseInt(parts[0]);
|
coefficient = Integer.parseInt(parts[0]);
|
||||||
coefficientExists = true;
|
coefficientExists = true;
|
||||||
}
|
}
|
||||||
if (parts.length > 1 && !parts[1].equals("") && parts[1] != null) {
|
if (parts.length > 1 && !parts[1].equals("") && parts[1] != null) {
|
||||||
constant = Integer.parseInt(parts[1]);
|
constant = Integer.parseInt(parts[1]);
|
||||||
constantExists = true;
|
constantExists = true;
|
||||||
}
|
}
|
||||||
} else if (element.contains("+")) {
|
} else if (element.contains("+")) {
|
||||||
constant = Integer.parseInt(element.replace("+", ""));
|
constant = Integer.parseInt(element.replace("+", ""));
|
||||||
constantExists = true;
|
constantExists = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i <= totalPages; i++) {
|
for (int i = 1; i <= totalPages; i++) {
|
||||||
int pageNum = coefficientExists ? coefficient * i : i;
|
int pageNum = coefficientExists ? coefficient * i : i;
|
||||||
pageNum += constantExists ? constant : 0;
|
pageNum += constantExists ? constant : 0;
|
||||||
|
|
||||||
if (pageNum <= totalPages && pageNum > 0) {
|
if (pageNum <= totalPages && pageNum > 0) {
|
||||||
newPageOrder.add(pageNum - 1);
|
newPageOrder.add(pageNum - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (element.contains("-")) {
|
} else if (element.contains("-")) {
|
||||||
// split the range into start and end page
|
// split the range into start and end page
|
||||||
String[] range = element.split("-");
|
String[] range = element.split("-");
|
||||||
int start = Integer.parseInt(range[0]);
|
int start = Integer.parseInt(range[0]);
|
||||||
int end = Integer.parseInt(range[1]);
|
int end = Integer.parseInt(range[1]);
|
||||||
// check if the end page is greater than total pages
|
// check if the end page is greater than total pages
|
||||||
if (end > totalPages) {
|
if (end > totalPages) {
|
||||||
end = totalPages;
|
end = totalPages;
|
||||||
}
|
}
|
||||||
// loop through the range of pages
|
// loop through the range of pages
|
||||||
for (int j = start; j <= end; j++) {
|
for (int j = start; j <= end; j++) {
|
||||||
// print the current index
|
// print the current index
|
||||||
newPageOrder.add(j - 1);
|
newPageOrder.add(j - 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if the element is a single page
|
// if the element is a single page
|
||||||
newPageOrder.add(Integer.parseInt(element) - 1);
|
newPageOrder.add(Integer.parseInt(element) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPageOrder;
|
return newPageOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean createDir(String path) {
|
public static boolean createDir(String path) {
|
||||||
Path folder = Paths.get(path);
|
Path folder = Paths.get(path);
|
||||||
if (!Files.exists(folder)) {
|
if (!Files.exists(folder)) {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(folder);
|
Files.createDirectories(folder);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,400 +1,400 @@
|
||||||
package stirling.software.SPDF.utils;
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageReader;
|
import javax.imageio.ImageReader;
|
||||||
import javax.imageio.ImageWriteParam;
|
import javax.imageio.ImageWriteParam;
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
|
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
import org.apache.pdfbox.rendering.ImageType;
|
import org.apache.pdfbox.rendering.ImageType;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.apache.pdfbox.text.PDFTextStripper;
|
import org.apache.pdfbox.text.PDFTextStripper;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import stirling.software.SPDF.pdf.ImageFinder;
|
import stirling.software.SPDF.pdf.ImageFinder;
|
||||||
|
|
||||||
public class PdfUtils {
|
public class PdfUtils {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
||||||
|
|
||||||
public static PDRectangle textToPageSize(String size) {
|
public static PDRectangle textToPageSize(String size) {
|
||||||
switch (size.toUpperCase()) {
|
switch (size.toUpperCase()) {
|
||||||
case "A0":
|
case "A0":
|
||||||
return PDRectangle.A0;
|
return PDRectangle.A0;
|
||||||
case "A1":
|
case "A1":
|
||||||
return PDRectangle.A1;
|
return PDRectangle.A1;
|
||||||
case "A2":
|
case "A2":
|
||||||
return PDRectangle.A2;
|
return PDRectangle.A2;
|
||||||
case "A3":
|
case "A3":
|
||||||
return PDRectangle.A3;
|
return PDRectangle.A3;
|
||||||
case "A4":
|
case "A4":
|
||||||
return PDRectangle.A4;
|
return PDRectangle.A4;
|
||||||
case "A5":
|
case "A5":
|
||||||
return PDRectangle.A5;
|
return PDRectangle.A5;
|
||||||
case "A6":
|
case "A6":
|
||||||
return PDRectangle.A6;
|
return PDRectangle.A6;
|
||||||
case "LETTER":
|
case "LETTER":
|
||||||
return PDRectangle.LETTER;
|
return PDRectangle.LETTER;
|
||||||
case "LEGAL":
|
case "LEGAL":
|
||||||
return PDRectangle.LEGAL;
|
return PDRectangle.LEGAL;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid standard page size: " + size);
|
throw new IllegalArgumentException("Invalid standard page size: " + size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasImages(PDDocument document, String pagesToCheck) throws IOException {
|
public static boolean hasImages(PDDocument document, String pagesToCheck) throws IOException {
|
||||||
String[] pageOrderArr = pagesToCheck.split(",");
|
String[] pageOrderArr = pagesToCheck.split(",");
|
||||||
List<Integer> pageList =
|
List<Integer> pageList =
|
||||||
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
|
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
|
||||||
|
|
||||||
for (int pageNumber : pageList) {
|
for (int pageNumber : pageList) {
|
||||||
PDPage page = document.getPage(pageNumber);
|
PDPage page = document.getPage(pageNumber);
|
||||||
if (hasImagesOnPage(page)) {
|
if (hasImagesOnPage(page)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasText(PDDocument document, String pageNumbersToCheck, String phrase)
|
public static boolean hasText(PDDocument document, String pageNumbersToCheck, String phrase)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String[] pageOrderArr = pageNumbersToCheck.split(",");
|
String[] pageOrderArr = pageNumbersToCheck.split(",");
|
||||||
List<Integer> pageList =
|
List<Integer> pageList =
|
||||||
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
|
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
|
||||||
|
|
||||||
for (int pageNumber : pageList) {
|
for (int pageNumber : pageList) {
|
||||||
PDPage page = document.getPage(pageNumber);
|
PDPage page = document.getPage(pageNumber);
|
||||||
if (hasTextOnPage(page, phrase)) {
|
if (hasTextOnPage(page, phrase)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasImagesOnPage(PDPage page) throws IOException {
|
public static boolean hasImagesOnPage(PDPage page) throws IOException {
|
||||||
ImageFinder imageFinder = new ImageFinder(page);
|
ImageFinder imageFinder = new ImageFinder(page);
|
||||||
imageFinder.processPage(page);
|
imageFinder.processPage(page);
|
||||||
return imageFinder.hasImages();
|
return imageFinder.hasImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasTextOnPage(PDPage page, String phrase) throws IOException {
|
public static boolean hasTextOnPage(PDPage page, String phrase) throws IOException {
|
||||||
PDFTextStripper textStripper = new PDFTextStripper();
|
PDFTextStripper textStripper = new PDFTextStripper();
|
||||||
PDDocument tempDoc = new PDDocument();
|
PDDocument tempDoc = new PDDocument();
|
||||||
tempDoc.addPage(page);
|
tempDoc.addPage(page);
|
||||||
String pageText = textStripper.getText(tempDoc);
|
String pageText = textStripper.getText(tempDoc);
|
||||||
tempDoc.close();
|
tempDoc.close();
|
||||||
return pageText.contains(phrase);
|
return pageText.contains(phrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsTextInFile(PDDocument pdfDocument, String text, String pagesToCheck)
|
public boolean containsTextInFile(PDDocument pdfDocument, String text, String pagesToCheck)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
PDFTextStripper textStripper = new PDFTextStripper();
|
PDFTextStripper textStripper = new PDFTextStripper();
|
||||||
String pdfText = "";
|
String pdfText = "";
|
||||||
|
|
||||||
if (pagesToCheck == null || pagesToCheck.equals("all")) {
|
if (pagesToCheck == null || pagesToCheck.equals("all")) {
|
||||||
pdfText = textStripper.getText(pdfDocument);
|
pdfText = textStripper.getText(pdfDocument);
|
||||||
} else {
|
} else {
|
||||||
// remove whitespaces
|
// remove whitespaces
|
||||||
pagesToCheck = pagesToCheck.replaceAll("\\s+", "");
|
pagesToCheck = pagesToCheck.replaceAll("\\s+", "");
|
||||||
|
|
||||||
String[] splitPoints = pagesToCheck.split(",");
|
String[] splitPoints = pagesToCheck.split(",");
|
||||||
for (String splitPoint : splitPoints) {
|
for (String splitPoint : splitPoints) {
|
||||||
if (splitPoint.contains("-")) {
|
if (splitPoint.contains("-")) {
|
||||||
// Handle page ranges
|
// Handle page ranges
|
||||||
String[] range = splitPoint.split("-");
|
String[] range = splitPoint.split("-");
|
||||||
int startPage = Integer.parseInt(range[0]);
|
int startPage = Integer.parseInt(range[0]);
|
||||||
int endPage = Integer.parseInt(range[1]);
|
int endPage = Integer.parseInt(range[1]);
|
||||||
|
|
||||||
for (int i = startPage; i <= endPage; i++) {
|
for (int i = startPage; i <= endPage; i++) {
|
||||||
textStripper.setStartPage(i);
|
textStripper.setStartPage(i);
|
||||||
textStripper.setEndPage(i);
|
textStripper.setEndPage(i);
|
||||||
pdfText += textStripper.getText(pdfDocument);
|
pdfText += textStripper.getText(pdfDocument);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle individual page
|
// Handle individual page
|
||||||
int page = Integer.parseInt(splitPoint);
|
int page = Integer.parseInt(splitPoint);
|
||||||
textStripper.setStartPage(page);
|
textStripper.setStartPage(page);
|
||||||
textStripper.setEndPage(page);
|
textStripper.setEndPage(page);
|
||||||
pdfText += textStripper.getText(pdfDocument);
|
pdfText += textStripper.getText(pdfDocument);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pdfDocument.close();
|
pdfDocument.close();
|
||||||
|
|
||||||
return pdfText.contains(text);
|
return pdfText.contains(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean pageCount(PDDocument pdfDocument, int pageCount, String comparator)
|
public boolean pageCount(PDDocument pdfDocument, int pageCount, String comparator)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
int actualPageCount = pdfDocument.getNumberOfPages();
|
int actualPageCount = pdfDocument.getNumberOfPages();
|
||||||
pdfDocument.close();
|
pdfDocument.close();
|
||||||
|
|
||||||
switch (comparator.toLowerCase()) {
|
switch (comparator.toLowerCase()) {
|
||||||
case "greater":
|
case "greater":
|
||||||
return actualPageCount > pageCount;
|
return actualPageCount > pageCount;
|
||||||
case "equal":
|
case "equal":
|
||||||
return actualPageCount == pageCount;
|
return actualPageCount == pageCount;
|
||||||
case "less":
|
case "less":
|
||||||
return actualPageCount < pageCount;
|
return actualPageCount < pageCount;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Invalid comparator. Only 'greater', 'equal', and 'less' are supported.");
|
"Invalid comparator. Only 'greater', 'equal', and 'less' are supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean pageSize(PDDocument pdfDocument, String expectedPageSize) throws IOException {
|
public boolean pageSize(PDDocument pdfDocument, String expectedPageSize) throws IOException {
|
||||||
PDPage firstPage = pdfDocument.getPage(0);
|
PDPage firstPage = pdfDocument.getPage(0);
|
||||||
PDRectangle mediaBox = firstPage.getMediaBox();
|
PDRectangle mediaBox = firstPage.getMediaBox();
|
||||||
|
|
||||||
float actualPageWidth = mediaBox.getWidth();
|
float actualPageWidth = mediaBox.getWidth();
|
||||||
float actualPageHeight = mediaBox.getHeight();
|
float actualPageHeight = mediaBox.getHeight();
|
||||||
|
|
||||||
pdfDocument.close();
|
pdfDocument.close();
|
||||||
|
|
||||||
// Assumes the expectedPageSize is in the format "widthxheight", e.g. "595x842" for A4
|
// Assumes the expectedPageSize is in the format "widthxheight", e.g. "595x842" for A4
|
||||||
String[] dimensions = expectedPageSize.split("x");
|
String[] dimensions = expectedPageSize.split("x");
|
||||||
float expectedPageWidth = Float.parseFloat(dimensions[0]);
|
float expectedPageWidth = Float.parseFloat(dimensions[0]);
|
||||||
float expectedPageHeight = Float.parseFloat(dimensions[1]);
|
float expectedPageHeight = Float.parseFloat(dimensions[1]);
|
||||||
|
|
||||||
// Checks if the actual page size matches the expected page size
|
// Checks if the actual page size matches the expected page size
|
||||||
return actualPageWidth == expectedPageWidth && actualPageHeight == expectedPageHeight;
|
return actualPageWidth == expectedPageWidth && actualPageHeight == expectedPageHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] convertFromPdf(
|
public static byte[] convertFromPdf(
|
||||||
byte[] inputStream,
|
byte[] inputStream,
|
||||||
String imageType,
|
String imageType,
|
||||||
ImageType colorType,
|
ImageType colorType,
|
||||||
boolean singleImage,
|
boolean singleImage,
|
||||||
int DPI,
|
int DPI,
|
||||||
String filename)
|
String filename)
|
||||||
throws IOException, Exception {
|
throws IOException, Exception {
|
||||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
int pageCount = document.getNumberOfPages();
|
int pageCount = document.getNumberOfPages();
|
||||||
|
|
||||||
// Create a ByteArrayOutputStream to save the image(s) to
|
// Create a ByteArrayOutputStream to save the image(s) to
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
if (singleImage) {
|
if (singleImage) {
|
||||||
if (imageType.toLowerCase().equals("tiff")
|
if (imageType.toLowerCase().equals("tiff")
|
||||||
|| imageType.toLowerCase().equals("tif")) {
|
|| imageType.toLowerCase().equals("tif")) {
|
||||||
// Write the images to the output stream as a TIFF with multiple frames
|
// Write the images to the output stream as a TIFF with multiple frames
|
||||||
ImageWriter writer = ImageIO.getImageWritersByFormatName("tiff").next();
|
ImageWriter writer = ImageIO.getImageWritersByFormatName("tiff").next();
|
||||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
param.setCompressionType("ZLib");
|
param.setCompressionType("ZLib");
|
||||||
param.setCompressionQuality(1.0f);
|
param.setCompressionQuality(1.0f);
|
||||||
|
|
||||||
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
|
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
|
||||||
writer.setOutput(ios);
|
writer.setOutput(ios);
|
||||||
writer.prepareWriteSequence(null);
|
writer.prepareWriteSequence(null);
|
||||||
|
|
||||||
for (int i = 0; i < pageCount; ++i) {
|
for (int i = 0; i < pageCount; ++i) {
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
||||||
writer.writeToSequence(new IIOImage(image, null, null), param);
|
writer.writeToSequence(new IIOImage(image, null, null), param);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.endWriteSequence();
|
writer.endWriteSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.dispose();
|
writer.dispose();
|
||||||
} else {
|
} else {
|
||||||
// Combine all images into a single big image
|
// Combine all images into a single big image
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(0, DPI, colorType);
|
BufferedImage image = pdfRenderer.renderImageWithDPI(0, DPI, colorType);
|
||||||
BufferedImage combined =
|
BufferedImage combined =
|
||||||
new BufferedImage(
|
new BufferedImage(
|
||||||
image.getWidth(),
|
image.getWidth(),
|
||||||
image.getHeight() * pageCount,
|
image.getHeight() * pageCount,
|
||||||
BufferedImage.TYPE_INT_RGB);
|
BufferedImage.TYPE_INT_RGB);
|
||||||
Graphics g = combined.getGraphics();
|
Graphics g = combined.getGraphics();
|
||||||
|
|
||||||
for (int i = 0; i < pageCount; ++i) {
|
for (int i = 0; i < pageCount; ++i) {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
||||||
}
|
}
|
||||||
g.drawImage(image, 0, i * image.getHeight(), null);
|
g.drawImage(image, 0, i * image.getHeight(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the image to the output stream
|
// Write the image to the output stream
|
||||||
ImageIO.write(combined, imageType, baos);
|
ImageIO.write(combined, imageType, baos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log that the image was successfully written to the byte array
|
// Log that the image was successfully written to the byte array
|
||||||
logger.info("Image successfully written to byte array");
|
logger.info("Image successfully written to byte array");
|
||||||
} else {
|
} else {
|
||||||
// Zip the images and return as byte array
|
// Zip the images and return as byte array
|
||||||
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
|
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
|
||||||
for (int i = 0; i < pageCount; ++i) {
|
for (int i = 0; i < pageCount; ++i) {
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
||||||
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
|
||||||
ImageIO.write(image, imageType, baosImage);
|
ImageIO.write(image, imageType, baosImage);
|
||||||
|
|
||||||
// Add the image to the zip file
|
// Add the image to the zip file
|
||||||
zos.putNextEntry(
|
zos.putNextEntry(
|
||||||
new ZipEntry(
|
new ZipEntry(
|
||||||
String.format(
|
String.format(
|
||||||
filename + "_%d.%s",
|
filename + "_%d.%s",
|
||||||
i + 1,
|
i + 1,
|
||||||
imageType.toLowerCase())));
|
imageType.toLowerCase())));
|
||||||
zos.write(baosImage.toByteArray());
|
zos.write(baosImage.toByteArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Log that the images were successfully written to the byte array
|
// Log that the images were successfully written to the byte array
|
||||||
logger.info("Images successfully written to byte array as a zip");
|
logger.info("Images successfully written to byte array as a zip");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Log an error message if there is an issue converting the PDF to an image
|
// Log an error message if there is an issue converting the PDF to an image
|
||||||
logger.error("Error converting PDF to image", e);
|
logger.error("Error converting PDF to image", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] imageToPdf(
|
public static byte[] imageToPdf(
|
||||||
MultipartFile[] files, String fitOption, boolean autoRotate, String colorType)
|
MultipartFile[] files, String fitOption, boolean autoRotate, String colorType)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (PDDocument doc = new PDDocument()) {
|
try (PDDocument doc = new PDDocument()) {
|
||||||
for (MultipartFile file : files) {
|
for (MultipartFile file : files) {
|
||||||
String contentType = file.getContentType();
|
String contentType = file.getContentType();
|
||||||
String originalFilename = file.getOriginalFilename();
|
String originalFilename = file.getOriginalFilename();
|
||||||
if (originalFilename != null
|
if (originalFilename != null
|
||||||
&& (originalFilename.toLowerCase().endsWith(".tiff")
|
&& (originalFilename.toLowerCase().endsWith(".tiff")
|
||||||
|| originalFilename.toLowerCase().endsWith(".tif"))) {
|
|| originalFilename.toLowerCase().endsWith(".tif"))) {
|
||||||
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
|
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
|
||||||
reader.setInput(ImageIO.createImageInputStream(file.getInputStream()));
|
reader.setInput(ImageIO.createImageInputStream(file.getInputStream()));
|
||||||
int numPages = reader.getNumImages(true);
|
int numPages = reader.getNumImages(true);
|
||||||
for (int i = 0; i < numPages; i++) {
|
for (int i = 0; i < numPages; i++) {
|
||||||
BufferedImage pageImage = reader.read(i);
|
BufferedImage pageImage = reader.read(i);
|
||||||
BufferedImage convertedImage =
|
BufferedImage convertedImage =
|
||||||
ImageProcessingUtils.convertColorType(pageImage, colorType);
|
ImageProcessingUtils.convertColorType(pageImage, colorType);
|
||||||
PDImageXObject pdImage =
|
PDImageXObject pdImage =
|
||||||
LosslessFactory.createFromImage(doc, convertedImage);
|
LosslessFactory.createFromImage(doc, convertedImage);
|
||||||
addImageToDocument(doc, pdImage, fitOption, autoRotate);
|
addImageToDocument(doc, pdImage, fitOption, autoRotate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
BufferedImage image = ImageIO.read(file.getInputStream());
|
BufferedImage image = ImageIO.read(file.getInputStream());
|
||||||
BufferedImage convertedImage =
|
BufferedImage convertedImage =
|
||||||
ImageProcessingUtils.convertColorType(image, colorType);
|
ImageProcessingUtils.convertColorType(image, colorType);
|
||||||
// Use JPEGFactory if it's JPEG since JPEG is lossy
|
// Use JPEGFactory if it's JPEG since JPEG is lossy
|
||||||
PDImageXObject pdImage =
|
PDImageXObject pdImage =
|
||||||
(contentType != null && contentType.equals("image/jpeg"))
|
(contentType != null && contentType.equals("image/jpeg"))
|
||||||
? JPEGFactory.createFromImage(doc, convertedImage)
|
? JPEGFactory.createFromImage(doc, convertedImage)
|
||||||
: LosslessFactory.createFromImage(doc, convertedImage);
|
: LosslessFactory.createFromImage(doc, convertedImage);
|
||||||
addImageToDocument(doc, pdImage, fitOption, autoRotate);
|
addImageToDocument(doc, pdImage, fitOption, autoRotate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
doc.save(byteArrayOutputStream);
|
doc.save(byteArrayOutputStream);
|
||||||
logger.info("PDF successfully saved to byte array");
|
logger.info("PDF successfully saved to byte array");
|
||||||
return byteArrayOutputStream.toByteArray();
|
return byteArrayOutputStream.toByteArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addImageToDocument(
|
private static void addImageToDocument(
|
||||||
PDDocument doc, PDImageXObject image, String fitOption, boolean autoRotate)
|
PDDocument doc, PDImageXObject image, String fitOption, boolean autoRotate)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
||||||
PDRectangle pageSize = PDRectangle.A4;
|
PDRectangle pageSize = PDRectangle.A4;
|
||||||
|
|
||||||
System.out.println(fitOption);
|
System.out.println(fitOption);
|
||||||
|
|
||||||
if (autoRotate && imageIsLandscape) {
|
if (autoRotate && imageIsLandscape) {
|
||||||
pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth());
|
pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("fitDocumentToImage".equals(fitOption)) {
|
if ("fitDocumentToImage".equals(fitOption)) {
|
||||||
pageSize = new PDRectangle(image.getWidth(), image.getHeight());
|
pageSize = new PDRectangle(image.getWidth(), image.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
PDPage page = new PDPage(pageSize);
|
PDPage page = new PDPage(pageSize);
|
||||||
doc.addPage(page);
|
doc.addPage(page);
|
||||||
|
|
||||||
float pageWidth = page.getMediaBox().getWidth();
|
float pageWidth = page.getMediaBox().getWidth();
|
||||||
float pageHeight = page.getMediaBox().getHeight();
|
float pageHeight = page.getMediaBox().getHeight();
|
||||||
|
|
||||||
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
||||||
if ("fillPage".equals(fitOption) || "fitDocumentToImage".equals(fitOption)) {
|
if ("fillPage".equals(fitOption) || "fitDocumentToImage".equals(fitOption)) {
|
||||||
contentStream.drawImage(image, 0, 0, pageWidth, pageHeight);
|
contentStream.drawImage(image, 0, 0, pageWidth, pageHeight);
|
||||||
} else if ("maintainAspectRatio".equals(fitOption)) {
|
} else if ("maintainAspectRatio".equals(fitOption)) {
|
||||||
float imageAspectRatio = (float) image.getWidth() / (float) image.getHeight();
|
float imageAspectRatio = (float) image.getWidth() / (float) image.getHeight();
|
||||||
float pageAspectRatio = pageWidth / pageHeight;
|
float pageAspectRatio = pageWidth / pageHeight;
|
||||||
|
|
||||||
float scaleFactor = 1.0f;
|
float scaleFactor = 1.0f;
|
||||||
if (imageAspectRatio > pageAspectRatio) {
|
if (imageAspectRatio > pageAspectRatio) {
|
||||||
scaleFactor = pageWidth / image.getWidth();
|
scaleFactor = pageWidth / image.getWidth();
|
||||||
} else {
|
} else {
|
||||||
scaleFactor = pageHeight / image.getHeight();
|
scaleFactor = pageHeight / image.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
float xPos = (pageWidth - (image.getWidth() * scaleFactor)) / 2;
|
float xPos = (pageWidth - (image.getWidth() * scaleFactor)) / 2;
|
||||||
float yPos = (pageHeight - (image.getHeight() * scaleFactor)) / 2;
|
float yPos = (pageHeight - (image.getHeight() * scaleFactor)) / 2;
|
||||||
contentStream.drawImage(
|
contentStream.drawImage(
|
||||||
image,
|
image,
|
||||||
xPos,
|
xPos,
|
||||||
yPos,
|
yPos,
|
||||||
image.getWidth() * scaleFactor,
|
image.getWidth() * scaleFactor,
|
||||||
image.getHeight() * scaleFactor);
|
image.getHeight() * scaleFactor);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error adding image to PDF", e);
|
logger.error("Error adding image to PDF", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] overlayImage(
|
public static byte[] overlayImage(
|
||||||
byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage)
|
byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
||||||
|
|
||||||
// Get the first page of the PDF
|
// Get the first page of the PDF
|
||||||
int pages = document.getNumberOfPages();
|
int pages = document.getNumberOfPages();
|
||||||
for (int i = 0; i < pages; i++) {
|
for (int i = 0; i < pages; i++) {
|
||||||
PDPage page = document.getPage(i);
|
PDPage page = document.getPage(i);
|
||||||
try (PDPageContentStream contentStream =
|
try (PDPageContentStream contentStream =
|
||||||
new PDPageContentStream(
|
new PDPageContentStream(
|
||||||
document, page, PDPageContentStream.AppendMode.APPEND, true)) {
|
document, page, PDPageContentStream.AppendMode.APPEND, true)) {
|
||||||
// Create an image object from the image bytes
|
// Create an image object from the image bytes
|
||||||
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
|
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
|
||||||
// Draw the image onto the page at the specified x and y coordinates
|
// Draw the image onto the page at the specified x and y coordinates
|
||||||
contentStream.drawImage(image, x, y);
|
contentStream.drawImage(image, x, y);
|
||||||
logger.info("Image successfully overlayed onto PDF");
|
logger.info("Image successfully overlayed onto PDF");
|
||||||
if (!everyPage && i == 0) {
|
if (!everyPage && i == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Log an error message if there is an issue overlaying the image onto the PDF
|
// Log an error message if there is an issue overlaying the image onto the PDF
|
||||||
logger.error("Error overlaying image onto PDF", e);
|
logger.error("Error overlaying image onto PDF", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create a ByteArrayOutputStream to save the PDF to
|
// Create a ByteArrayOutputStream to save the PDF to
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
document.save(baos);
|
document.save(baos);
|
||||||
logger.info("PDF successfully saved to byte array");
|
logger.info("PDF successfully saved to byte array");
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +1,67 @@
|
||||||
package stirling.software.SPDF.utils;
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
public class WebResponseUtils {
|
public class WebResponseUtils {
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> boasToWebResponse(
|
public static ResponseEntity<byte[]> boasToWebResponse(
|
||||||
ByteArrayOutputStream baos, String docName) throws IOException {
|
ByteArrayOutputStream baos, String docName) throws IOException {
|
||||||
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> boasToWebResponse(
|
public static ResponseEntity<byte[]> boasToWebResponse(
|
||||||
ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
|
ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
|
||||||
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
|
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> multiPartFileToWebResponse(MultipartFile file)
|
public static ResponseEntity<byte[]> multiPartFileToWebResponse(MultipartFile file)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String fileName = file.getOriginalFilename();
|
String fileName = file.getOriginalFilename();
|
||||||
MediaType mediaType = MediaType.parseMediaType(file.getContentType());
|
MediaType mediaType = MediaType.parseMediaType(file.getContentType());
|
||||||
|
|
||||||
byte[] bytes = file.getBytes();
|
byte[] bytes = file.getBytes();
|
||||||
|
|
||||||
return bytesToWebResponse(bytes, fileName, mediaType);
|
return bytesToWebResponse(bytes, fileName, mediaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> bytesToWebResponse(
|
public static ResponseEntity<byte[]> bytesToWebResponse(
|
||||||
byte[] bytes, String docName, MediaType mediaType) throws IOException {
|
byte[] bytes, String docName, MediaType mediaType) throws IOException {
|
||||||
|
|
||||||
// Return the PDF as a response
|
// Return the PDF as a response
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(mediaType);
|
headers.setContentType(mediaType);
|
||||||
headers.setContentLength(bytes.length);
|
headers.setContentLength(bytes.length);
|
||||||
String encodedDocName =
|
String encodedDocName =
|
||||||
URLEncoder.encode(docName, StandardCharsets.UTF_8.toString())
|
URLEncoder.encode(docName, StandardCharsets.UTF_8.toString())
|
||||||
.replaceAll("\\+", "%20");
|
.replaceAll("\\+", "%20");
|
||||||
headers.setContentDispositionFormData("attachment", encodedDocName);
|
headers.setContentDispositionFormData("attachment", encodedDocName);
|
||||||
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName)
|
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
|
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName)
|
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
// Open Byte Array and save document to it
|
// Open Byte Array and save document to it
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
document.save(baos);
|
document.save(baos);
|
||||||
// Close the document
|
// Close the document
|
||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
return boasToWebResponse(baos, docName);
|
return boasToWebResponse(baos, docName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue