commit for feature developing invert-replace color of a pdf for stirl… (#1982)
commit for feature developing invert-replace color of a pdf for stirling PDF
This commit is contained in:
parent
45e4c15d2d
commit
494bc2c09f
17 changed files with 697 additions and 2 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,13 @@ public class OtherWebController {
|
||||||
return "misc/compress-pdf";
|
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")
|
@GetMapping("/extract-image-scans")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView extractImageScansForm() {
|
public ModelAndView extractImageScansForm() {
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package stirling.software.SPDF.model.api.misc;
|
||||||
|
|
||||||
|
public enum ReplaceAndInvert {
|
||||||
|
HIGH_CONTRAST_COLOR,
|
||||||
|
CUSTOM_COLOR,
|
||||||
|
FULL_INVERSION,
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -480,6 +480,26 @@ home.removeImagePdf.title=Remove image
|
||||||
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
||||||
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
||||||
|
|
||||||
|
#replace-invert-color
|
||||||
|
replace-color.title=Replace-Invert-Color
|
||||||
|
replace-color.header=Replace-Invert Color PDF
|
||||||
|
home.replaceColorPdf.title=Replace and Invert Color
|
||||||
|
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
|
||||||
|
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
||||||
|
replace-color.selectText.1=Replace or Invert color Options
|
||||||
|
replace-color.selectText.2=Default(Default high contrast colors)
|
||||||
|
replace-color.selectText.3=Custom(Customized colors)
|
||||||
|
replace-color.selectText.4=Full-Invert(Invert all colors)
|
||||||
|
replace-color.selectText.5=High contrast color options
|
||||||
|
replace-color.selectText.6=white text on black background
|
||||||
|
replace-color.selectText.7=Black text on white background
|
||||||
|
replace-color.selectText.8=Yellow text on black background
|
||||||
|
replace-color.selectText.9=Green text on black background
|
||||||
|
replace-color.selectText.10=Choose text Color
|
||||||
|
replace-color.selectText.11=Choose background Color
|
||||||
|
replace-color.submit=Replace
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# #
|
# #
|
||||||
|
|
|
@ -480,6 +480,30 @@ home.removeImagePdf.title=Remove image
|
||||||
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
||||||
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
||||||
|
|
||||||
|
#replace-invert-color
|
||||||
|
replace-color.title=Replace-Invert-Color
|
||||||
|
replace-color.header=Replace-Invert Color PDF
|
||||||
|
home.replaceColorPdf.title=Replace and Invert Color
|
||||||
|
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
|
||||||
|
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
||||||
|
replace-color.selectText.1=Replace or Invert color Options
|
||||||
|
replace-color.selectText.2=Default(Default high contrast colors)
|
||||||
|
replace-color.selectText.3=Custom(Customized colors)
|
||||||
|
replace-color.selectText.4=Full-Invert(Invert all colors)
|
||||||
|
replace-color.selectText.5=High contrast color options
|
||||||
|
replace-color.selectText.6=white text on black background
|
||||||
|
replace-color.selectText.7=Black text on white background
|
||||||
|
replace-color.selectText.8=Yellow text on black background
|
||||||
|
replace-color.selectText.9=Green text on black background
|
||||||
|
replace-color.selectText.10=Choose text Color
|
||||||
|
replace-color.selectText.11=Choose background Color
|
||||||
|
replace-color.submit=Replace
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# #
|
# #
|
||||||
|
|
|
@ -198,6 +198,9 @@
|
||||||
<div
|
<div
|
||||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-image-pdf','remove_selection', 'home.removeImagePdf.title', 'home.removeImagePdf.desc', 'removeImagePdf.tags', 'other')}">
|
th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-image-pdf','remove_selection', 'home.removeImagePdf.title', 'home.removeImagePdf.desc', 'removeImagePdf.tags', 'other')}">
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
th:replace="~{fragments/navbarEntry :: navbarEntry ('replace-and-invert-color-pdf','format_color_fill', 'replace-color.title', 'home.replaceColorPdf.desc', 'replaceColorPdf.tags', 'other')}">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Advance menu items -->
|
<!-- Advance menu items -->
|
||||||
<div class="col-lg-2 col-sm-6 py px-xl-1 px-2">
|
<div class="col-lg-2 col-sm-6 py px-xl-1 px-2">
|
||||||
|
|
|
@ -248,11 +248,15 @@
|
||||||
<div
|
<div
|
||||||
th:replace="~{fragments/card :: card(id='remove-image-pdf', cardTitle=#{home.removeImagePdf.title}, cardText=#{home.removeImagePdf.desc}, cardLink='remove-image-pdf', toolIcon='remove_selection', tags=#{removeImagePdf.tags}, toolGroup='other')}">
|
th:replace="~{fragments/card :: card(id='remove-image-pdf', cardTitle=#{home.removeImagePdf.title}, cardText=#{home.removeImagePdf.desc}, cardLink='remove-image-pdf', toolIcon='remove_selection', tags=#{removeImagePdf.tags}, toolGroup='other')}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div
|
||||||
|
th:replace="~{fragments/card :: card(id='replace-color-pdf', cardTitle=#{home.replaceColorPdf.title}, cardText=#{home.replaceColorPdf.desc}, cardLink='replace-and-invert-color-pdf', toolIcon='format_color_fill', tags=#{replaceColorPdf.tags}, toolGroup='other')}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="groupAdvanced" class="feature-group">
|
<div id="groupAdvanced" class="feature-group">
|
||||||
<div
|
<div
|
||||||
|
|
||||||
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.advance})}">
|
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.advance})}">
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-group-container">
|
<div class="feature-group-container">
|
||||||
|
|
89
src/main/resources/templates/misc/replace-color.html
Normal file
89
src/main/resources/templates/misc/replace-color.html
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||||
|
xmlns:th="https://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<th:block th:insert="~{fragments/common :: head(title=#{replace-color.title}, header=#{replace-color.header})}"></th:block>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
|
<br><br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6 bg-card">
|
||||||
|
<div class="tool-header">
|
||||||
|
<span class="material-symbols-rounded tool-header-icon advance">zoom_in_map</span>
|
||||||
|
<span class="tool-header-text" th:text="#{replace-color.header}"></span>
|
||||||
|
</div>
|
||||||
|
<form action="#" th:action="@{'/api/v1/misc/replace-invert-pdf'}" method="post" enctype="multipart/form-data">
|
||||||
|
<div
|
||||||
|
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||||
|
</div>
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 th:text="#{replace-color.selectText.1}"></h4>
|
||||||
|
<select name="replaceAndInvertOption" id="replace-invert" class="form-control">
|
||||||
|
<option value="HIGH_CONTRAST_COLOR" th:text="#{replace-color.selectText.2}" ></option>
|
||||||
|
<option value="CUSTOM_COLOR" th:text="#{replace-color.selectText.3}"></option>
|
||||||
|
<option value="FULL_INVERSION" th:text="#{replace-color.selectText.4}" selected></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card mb-3" id="high-contrast-options" style="display: none">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 th:text="#{replace-color.selectText.5}"></h4>
|
||||||
|
<select name="highContrastColorCombination" id="high-contrast" class="form-control">
|
||||||
|
<option value="WHITE_TEXT_ON_BLACK" th:text="#{replace-color.selectText.6}" selected></option>
|
||||||
|
<option value="BLACK_TEXT_ON_WHITE" th:text="#{replace-color.selectText.7}"></option>
|
||||||
|
<option value="YELLOW_TEXT_ON_BLACK" th:text="#{replace-color.selectText.8}"></option>
|
||||||
|
<option value="GREEN_TEXT_ON_BLACK" th:text="#{replace-color.selectText.9}"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card mb-3" id = "custom-color-1" style="display: none">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 th:text="#{replace-color.selectText.10}"></h4>
|
||||||
|
<input type="color" name="textColor" id="text-color" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card mb-3" id = "custom-color-2" style="display: none">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 th:text="#{replace-color.selectText.11}"></h4>
|
||||||
|
<input type="color" name="backGroundColor" id="bg-color" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{replace-color.submit}"></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#replace-invert').change(function() {
|
||||||
|
var selectedOption = $(this).val();
|
||||||
|
|
||||||
|
// Hide all conditional fields by default
|
||||||
|
$('#high-contrast-options').hide();
|
||||||
|
$('#custom-color-1').hide();
|
||||||
|
$('#custom-color-2').hide();
|
||||||
|
|
||||||
|
if (selectedOption === "HIGH_CONTRAST_COLOR") {
|
||||||
|
$('#high-contrast-options').show();
|
||||||
|
} else if (selectedOption === "CUSTOM_COLOR") {
|
||||||
|
$('#custom-color-1').show();
|
||||||
|
$('#custom-color-2').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in a new issue