Merge remote-tracking branch 'origin/main' into Frooodle/license

This commit is contained in:
Anthony Stirling
2024-10-05 09:21:08 +01:00
68 changed files with 1796 additions and 249 deletions

View File

@@ -0,0 +1,39 @@
package stirling.software.SPDF.Factories;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
import stirling.software.SPDF.utils.misc.CustomColorReplaceStrategy;
import stirling.software.SPDF.utils.misc.InvertFullColorStrategy;
import stirling.software.SPDF.utils.misc.ReplaceAndInvertColorStrategy;
@Component
public class ReplaceAndInvertColorFactory {
public ReplaceAndInvertColorStrategy replaceAndInvert(
MultipartFile file,
ReplaceAndInvert replaceAndInvertOption,
HighContrastColorCombination highContrastColorCombination,
String backGroundColor,
String textColor) {
if (replaceAndInvertOption == ReplaceAndInvert.CUSTOM_COLOR
|| replaceAndInvertOption == ReplaceAndInvert.HIGH_CONTRAST_COLOR) {
return new CustomColorReplaceStrategy(
file,
replaceAndInvertOption,
textColor,
backGroundColor,
highContrastColorCombination);
} else if (replaceAndInvertOption == ReplaceAndInvert.FULL_INVERSION) {
return new InvertFullColorStrategy(file, replaceAndInvertOption);
}
return null;
}
}

View File

@@ -1,6 +1,7 @@
package stirling.software.SPDF;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -30,14 +31,36 @@ public class SPdfApplication {
private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class);
@Autowired private Environment env;
@Autowired ApplicationProperties applicationProperties;
private static String serverPortStatic;
@Value("${server.port:8080}")
public void setServerPortStatic(String port) {
SPdfApplication.serverPortStatic = port;
if (port.equalsIgnoreCase("auto")) {
// Use Spring Boot's automatic port assignment (server.port=0)
SPdfApplication.serverPortStatic =
"0"; // This will let Spring Boot assign an available port
} else {
SPdfApplication.serverPortStatic = port;
}
}
// Optionally keep this method if you want to provide a manual port-incrementation fallback.
private static String findAvailablePort(int startPort) {
int port = startPort;
while (!isPortAvailable(port)) {
port++;
}
return String.valueOf(port);
}
private static boolean isPortAvailable(int port) {
try (ServerSocket socket = new ServerSocket(port)) {
return true;
} catch (IOException e) {
return false;
}
}
@PostConstruct
@@ -47,13 +70,17 @@ public class SPdfApplication {
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) {
try {
String url = "http://localhost:" + getNonStaticPort();
String url = "http://localhost:" + getStaticPort();
String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime();
if (os.contains("win")) {
// For Windows
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
} else if (os.contains("mac")) {
rt.exec("open " + url);
} else if (os.contains("nix") || os.contains("nux")) {
rt.exec("xdg-open " + url);
}
} catch (Exception e) {
logger.error("Error opening browser: {}", e.getMessage());
@@ -69,15 +96,13 @@ public class SPdfApplication {
app.addInitializers(new ConfigInitializer());
Map<String, String> propertyFiles = new HashMap<>();
// stirling pdf settings file
// External config files
if (Files.exists(Paths.get("configs/settings.yml"))) {
propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml");
} else {
logger.warn(
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
logger.warn("External configuration file 'configs/settings.yml' does not exist.");
}
// custom javs settings file
if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
String existingLocation =
propertyFiles.getOrDefault("spring.config.additional-location", "");
@@ -100,19 +125,14 @@ public class SPdfApplication {
app.run(args);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Thread interrupted while sleeping", e);
}
// Ensure directories are created
try {
Files.createDirectories(Path.of("customFiles/static/"));
Files.createDirectories(Path.of("customFiles/templates/"));
} catch (Exception e) {
logger.error("Error creating directories: {}", e.getMessage());
}
printStartupLogs();
}

View File

@@ -67,15 +67,6 @@ public class SplitPdfByChaptersController {
}
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
// checks if the document is encrypted by an empty user password
if (sourceDocument.isEncrypted()) {
try {
sourceDocument.setAllSecurityToBeRemoved(true);
logger.info("Removing security from the source document ");
} catch (Exception e) {
logger.warn("Cannot decrypt the pdf");
}
}
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
if (outline == null) {

View File

@@ -0,0 +1,55 @@
package stirling.software.SPDF.controller.api.misc;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvertColorRequest;
import stirling.software.SPDF.service.misc.ReplaceAndInvertColorService;
@RestController
@RequestMapping("/api/v1/misc")
public class ReplaceAndInvertColorController {
private ReplaceAndInvertColorService replaceAndInvertColorService;
@Autowired
public ReplaceAndInvertColorController(
ReplaceAndInvertColorService replaceAndInvertColorService) {
this.replaceAndInvertColorService = replaceAndInvertColorService;
}
@PostMapping(consumes = "multipart/form-data", value = "/replace-invert-pdf")
@Operation(
summary = "Replace-Invert Color PDF",
description =
"This endpoint accepts a PDF file and option of invert all colors or replace text and background colors. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<InputStreamResource> replaceAndInvertColor(
@ModelAttribute ReplaceAndInvertColorRequest replaceAndInvertColorRequest)
throws IOException {
InputStreamResource resource =
replaceAndInvertColorService.replaceAndInvertColor(
replaceAndInvertColorRequest.getFileInput(),
replaceAndInvertColorRequest.getReplaceAndInvertOption(),
replaceAndInvertColorRequest.getHighContrastColorCombination(),
replaceAndInvertColorRequest.getBackGroundColor(),
replaceAndInvertColorRequest.getTextColor());
// Return the modified PDF as a downloadable file
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=inverted.pdf")
.contentType(MediaType.APPLICATION_PDF)
.body(resource);
}
}

View File

@@ -15,7 +15,7 @@ import stirling.software.SPDF.utils.CheckProgramInstall;
@Tag(name = "Convert", description = "Convert APIs")
public class ConverterWebController {
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
@ConditionalOnExpression("${bookAndHtmlFormatsInstalled}")
@GetMapping("/book-to-pdf")
@Hidden
public String convertBookToPdfForm(Model model) {
@@ -60,7 +60,7 @@ public class ConverterWebController {
// PDF TO......
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
@ConditionalOnExpression("${bookAndHtmlFormatsInstalled}")
@GetMapping("/pdf-to-book")
@Hidden
public String convertPdfToBookForm(Model model) {

View File

@@ -31,6 +31,13 @@ public class OtherWebController {
return "misc/compress-pdf";
}
@GetMapping("/replace-and-invert-color-pdf")
@Hidden
public String replaceAndInvertColorPdfForm(Model model) {
model.addAttribute("currentPage", "replace-invert-color-pdf");
return "misc/replace-color";
}
@GetMapping("/extract-image-scans")
@Hidden
public ModelAndView extractImageScansForm() {

View File

@@ -0,0 +1,8 @@
package stirling.software.SPDF.model.api.misc;
public enum HighContrastColorCombination {
WHITE_TEXT_ON_BLACK,
BLACK_TEXT_ON_WHITE,
YELLOW_TEXT_ON_BLACK,
GREEN_TEXT_ON_BLACK,
}

View File

@@ -0,0 +1,7 @@
package stirling.software.SPDF.model.api.misc;
public enum ReplaceAndInvert {
HIGH_CONTRAST_COLOR,
CUSTOM_COLOR,
FULL_INVERSION,
}

View File

@@ -0,0 +1,40 @@
package stirling.software.SPDF.model.api.misc;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class ReplaceAndInvertColorRequest extends PDFFile {
@Schema(
description = "Replace and Invert color options of a pdf.",
allowableValues = {"HIGH_CONTRAST_COLOR", "CUSTOM_COLOR", "FULL_INVERSION"})
private ReplaceAndInvert replaceAndInvertOption;
@Schema(
description =
"If HIGH_CONTRAST_COLOR option selected, then pick the default color option for text and background.",
allowableValues = {
"WHITE_TEXT_ON_BLACK",
"BLACK_TEXT_ON_WHITE",
"YELLOW_TEXT_ON_BLACK",
"GREEN_TEXT_ON_BLACK"
})
private HighContrastColorCombination highContrastColorCombination;
@Schema(
description =
"If CUSTOM_COLOR option selected, then pick the custom color for background. "
+ "Expected color value should be 24bit decimal value of a color")
private String backGroundColor;
@Schema(
description =
"If CUSTOM_COLOR option selected, then pick the custom color for text. "
+ "Expected color value should be 24bit decimal value of a color")
private String textColor;
}

View File

@@ -7,6 +7,8 @@ import java.io.InputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@@ -17,6 +19,8 @@ import stirling.software.SPDF.model.api.PDFFile;
@Component
public class CustomPDDocumentFactory {
private static final Logger logger = LoggerFactory.getLogger(CustomPDDocumentFactory.class);
private final PdfMetadataService pdfMetadataService;
@Autowired
@@ -70,6 +74,7 @@ public class CustomPDDocumentFactory {
public PDDocument load(byte[] input) throws IOException {
PDDocument document = Loader.loadPDF(input);
pdfMetadataService.setDefaultMetadata(document);
removezeropassword(document);
return document;
}
@@ -95,5 +100,17 @@ public class CustomPDDocumentFactory {
return document;
}
private PDDocument removezeropassword(PDDocument document) throws IOException {
if (document.isEncrypted()) {
try {
logger.info("Removing security from the source document");
document.setAllSecurityToBeRemoved(true);
} catch (Exception e) {
logger.warn("Cannot decrypt the pdf");
}
}
return document;
}
// Add other load methods as needed, following the same pattern
}

View File

@@ -0,0 +1,42 @@
package stirling.software.SPDF.service.misc;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.Factories.ReplaceAndInvertColorFactory;
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
import stirling.software.SPDF.utils.misc.ReplaceAndInvertColorStrategy;
@Service
public class ReplaceAndInvertColorService {
private ReplaceAndInvertColorFactory replaceAndInvertColorFactory;
@Autowired
public ReplaceAndInvertColorService(ReplaceAndInvertColorFactory replaceAndInvertColorFactory) {
this.replaceAndInvertColorFactory = replaceAndInvertColorFactory;
}
public InputStreamResource replaceAndInvertColor(
MultipartFile file,
ReplaceAndInvert replaceAndInvertOption,
HighContrastColorCombination highContrastColorCombination,
String backGroundColor,
String textColor)
throws IOException {
ReplaceAndInvertColorStrategy replaceColorStrategy =
replaceAndInvertColorFactory.replaceAndInvert(
file,
replaceAndInvertOption,
highContrastColorCombination,
backGroundColor,
textColor);
return replaceColorStrategy.replace();
}
}

View File

@@ -0,0 +1,163 @@
package stirling.software.SPDF.utils.misc;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.font.*;
import org.apache.pdfbox.text.TextPosition;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
private String textColor;
private String backgroundColor;
private HighContrastColorCombination highContrastColorCombination;
public CustomColorReplaceStrategy(
MultipartFile file,
ReplaceAndInvert replaceAndInvert,
String textColor,
String backgroundColor,
HighContrastColorCombination highContrastColorCombination) {
super(file, replaceAndInvert);
this.textColor = textColor;
this.backgroundColor = backgroundColor;
this.highContrastColorCombination = highContrastColorCombination;
}
@Override
public InputStreamResource replace() throws IOException {
// If ReplaceAndInvert is HighContrastColor option, then get the colors of text and
// background from static
if (replaceAndInvert == ReplaceAndInvert.HIGH_CONTRAST_COLOR) {
String[] colors =
HighContrastColorReplaceDecider.getColors(
replaceAndInvert, highContrastColorCombination);
this.textColor = colors[0];
this.backgroundColor = colors[1];
}
// Create a temporary file, with the original filename from the multipart file
File file = File.createTempFile("temp", getFileInput().getOriginalFilename());
// Transfer the content of the multipart file to the file
getFileInput().transferTo(file);
try (PDDocument document = Loader.loadPDF(file)) {
PDPageTree pages = document.getPages();
for (PDPage page : pages) {
PdfTextStripperCustom pdfTextStripperCustom = new PdfTextStripperCustom();
// Get text positions
List<List<TextPosition>> charactersByArticle =
pdfTextStripperCustom.processPageCustom(page);
// Begin a new content stream
PDPageContentStream contentStream =
new PDPageContentStream(
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
// Set the new text color
contentStream.setNonStrokingColor(Color.decode(this.textColor));
// Draw the text with the new color
for (List<TextPosition> textPositions : charactersByArticle) {
for (TextPosition text : textPositions) {
// Move to the text position
contentStream.beginText();
contentStream.newLineAtOffset(
text.getX(), page.getMediaBox().getHeight() - text.getY());
PDFont font = null;
String unicodeText = text.getUnicode();
try {
font = PDFontFactory.createFont(text.getFont().getCOSObject());
} catch (IOException io) {
System.out.println("Primary font not found, using fallback font.");
font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
}
// if a character is not supported by font, then look for supported font
try {
byte[] bytes = font.encode(unicodeText);
} catch (IOException io) {
System.out.println("text could not be encoded ");
font = checkSupportedFontForCharacter(unicodeText);
} catch (IllegalArgumentException ie) {
System.out.println("text not supported by font ");
font = checkSupportedFontForCharacter(unicodeText);
} finally {
// if any other font is not supported, then replace default character *
if (font == null) {
font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
unicodeText = "*";
}
}
contentStream.setFont(font, text.getFontSize());
contentStream.showText(unicodeText);
contentStream.endText();
}
}
// Close the content stream
contentStream.close();
// Use a content stream to overlay the background color
try (PDPageContentStream contentStreamBg =
new PDPageContentStream(
document,
page,
PDPageContentStream.AppendMode.PREPEND,
true,
true)) {
// Set background color (e.g., light yellow)
contentStreamBg.setNonStrokingColor(Color.decode(this.backgroundColor));
contentStreamBg.addRect(
0, 0, page.getMediaBox().getWidth(), page.getMediaBox().getHeight());
contentStreamBg.fill();
}
}
// Save the modified PDF to a ByteArrayOutputStream
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
document.save(byteArrayOutputStream);
document.close();
// Prepare the modified PDF for download
ByteArrayInputStream inputStream =
new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
InputStreamResource resource = new InputStreamResource(inputStream);
return resource;
}
}
private PDFont checkSupportedFontForCharacter(String unicodeText) {
Set<String> fonts = Standard14Fonts.getNames();
for (String font : fonts) {
Standard14Fonts.FontName fontName = Standard14Fonts.getMappedFontName(font);
PDFont currentFont = new PDType1Font(fontName);
try {
byte[] bytes = currentFont.encode(unicodeText);
return currentFont;
} catch (IOException io) {
System.out.println("text could not be encoded ");
} catch (IllegalArgumentException ie) {
System.out.println("text not supported by font ");
}
}
return null;
}
}

View File

@@ -0,0 +1,30 @@
package stirling.software.SPDF.utils.misc;
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
public class HighContrastColorReplaceDecider {
// To decide the text and background colors for High contrast color option for replace-invert
// color feature
public static String[] getColors(
ReplaceAndInvert replaceAndInvert,
HighContrastColorCombination highContrastColorCombination) {
if (highContrastColorCombination == HighContrastColorCombination.BLACK_TEXT_ON_WHITE) {
return new String[] {"0", "16777215"};
} else if (highContrastColorCombination
== HighContrastColorCombination.GREEN_TEXT_ON_BLACK) {
return new String[] {"65280", "0"};
} else if (highContrastColorCombination
== HighContrastColorCombination.WHITE_TEXT_ON_BLACK) {
return new String[] {"16777215", "0"};
} else if (highContrastColorCombination
== HighContrastColorCombination.YELLOW_TEXT_ON_BLACK) {
return new String[] {"16776960", "0"};
}
return null;
}
}

View File

@@ -0,0 +1,104 @@
package stirling.software.SPDF.utils.misc;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
public InvertFullColorStrategy(MultipartFile file, ReplaceAndInvert replaceAndInvert) {
super(file, replaceAndInvert);
}
@Override
public InputStreamResource replace() throws IOException {
// Create a temporary file, with the original filename from the multipart file
File file = File.createTempFile("temp", getFileInput().getOriginalFilename());
// Transfer the content of the multipart file to the file
getFileInput().transferTo(file);
// Load the uploaded PDF
PDDocument document = Loader.loadPDF(file);
// Render each page and invert colors
PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); page++) {
BufferedImage image =
pdfRenderer.renderImageWithDPI(page, 300); // Render page at 300 DPI
// Invert the colors
invertImageColors(image);
// Create a new PDPage from the inverted image
PDPage pdPage = document.getPage(page);
PDImageXObject pdImage =
PDImageXObject.createFromFileByContent(
convertToBufferedImageTpFile(image), document);
PDPageContentStream contentStream =
new PDPageContentStream(
document, pdPage, PDPageContentStream.AppendMode.OVERWRITE, true);
contentStream.drawImage(
pdImage,
0,
0,
pdPage.getMediaBox().getWidth(),
pdPage.getMediaBox().getHeight());
contentStream.close();
}
// Save the modified PDF to a ByteArrayOutputStream
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
document.save(byteArrayOutputStream);
document.close();
// Prepare the modified PDF for download
ByteArrayInputStream inputStream =
new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
InputStreamResource resource = new InputStreamResource(inputStream);
return resource;
}
// Method to invert image colors
private void invertImageColors(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int rgba = image.getRGB(x, y);
Color color = new Color(rgba, true);
Color invertedColor =
new Color(
255 - color.getRed(),
255 - color.getGreen(),
255 - color.getBlue());
image.setRGB(x, y, invertedColor.getRGB());
}
}
}
// Helper method to convert BufferedImage to InputStream
private File convertToBufferedImageTpFile(BufferedImage image) throws IOException {
File file = new File("image.png");
ImageIO.write(image, "png", file);
return file;
}
}

View File

@@ -0,0 +1,36 @@
package stirling.software.SPDF.utils.misc;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.text.PDFTextStripperByArea;
import org.apache.pdfbox.text.TextPosition;
public class PdfTextStripperCustom extends PDFTextStripperByArea {
/**
* Constructor.
*
* @throws IOException If there is an error loading properties.
*/
public PdfTextStripperCustom() throws IOException {}
// To process the page text using stripper and returns the TextPosition and its values
public List<List<TextPosition>> processPageCustom(PDPage page) throws IOException {
addRegion(
"wholePage",
new Rectangle2D.Float(
page.getMediaBox().getLowerLeftX(),
page.getMediaBox().getLowerLeftY(),
page.getMediaBox().getWidth(),
page.getMediaBox().getHeight()));
extractRegions(page);
List<List<TextPosition>> textPositions = getCharactersByArticle();
return textPositions;
}
}

View File

@@ -0,0 +1,24 @@
package stirling.software.SPDF.utils.misc;
import java.io.IOException;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
@Data
// @EqualsAndHashCode(callSuper = true)
public abstract class ReplaceAndInvertColorStrategy extends PDFFile {
protected ReplaceAndInvert replaceAndInvert;
public ReplaceAndInvertColorStrategy(MultipartFile file, ReplaceAndInvert replaceAndInvert) {
setFileInput(file);
setReplaceAndInvert(replaceAndInvert);
}
public abstract InputStreamResource replace() throws IOException;
}