some more changes also broke pipeline a bit
This commit is contained in:
parent
6e32c7fe85
commit
9af1b0cfdc
7 changed files with 430 additions and 313 deletions
|
@ -1,162 +1,171 @@
|
||||||
package stirling.software.SPDF.controller.api.filters;
|
package stirling.software.SPDF.controller.api.filters;
|
||||||
|
|
||||||
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.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.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestPart;
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@RestController
|
@RestController
|
||||||
@Tag(name = "Filter", description = "Filter APIs")
|
@Tag(name = "Filter", description = "Filter APIs")
|
||||||
public class FilterController {
|
public class FilterController {
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/contains-text")
|
@PostMapping(consumes = "multipart/form-data", value = "/contains-text")
|
||||||
@Operation(summary = "Checks if a PDF contains set text, returns true if does", description = "Input:PDF Output:Boolean Type:SISO")
|
@Operation(summary = "Checks if a PDF contains set text, returns true if does", description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
public Boolean containsText(
|
public Boolean containsText(
|
||||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile,
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile,
|
||||||
@Parameter(description = "The text to check for", required = true) String text,
|
@Parameter(description = "The text to check for", required = true) String text,
|
||||||
@Parameter(description = "The page number to check for text on accepts 'All', ranges like '1-4'", required = false) String pageNumber)
|
@Parameter(description = "The page number to check for text on accepts 'All', ranges like '1-4'", required = false) String pageNumber)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
||||||
return PdfUtils.hasText(pdfDocument, pageNumber);
|
return PdfUtils.hasText(pdfDocument, pageNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/contains-image")
|
//TODO
|
||||||
@Operation(summary = "Checks if a PDF contains an image", description = "Input:PDF Output:Boolean Type:SISO")
|
@PostMapping(consumes = "multipart/form-data", value = "/contains-image")
|
||||||
public Boolean containsImage(
|
@Operation(summary = "Checks if a PDF contains an image", description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile,
|
public Boolean containsImage(
|
||||||
@Parameter(description = "The page number to check for image on accepts 'All', ranges like '1-4'", required = false) String pageNumber)
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile,
|
||||||
throws IOException, InterruptedException {
|
@Parameter(description = "The page number to check for image on accepts 'All', ranges like '1-4'", required = false) String pageNumber)
|
||||||
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
throws IOException, InterruptedException {
|
||||||
return PdfUtils.hasImagesOnPage(null);
|
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
||||||
}
|
return PdfUtils.hasImagesOnPage(null);
|
||||||
|
}
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/page-count")
|
|
||||||
@Operation(summary = "Checks if a PDF is greater, less or equal to a setPageCount", description = "Input:PDF Output:Boolean Type:SISO")
|
@PostMapping(consumes = "multipart/form-data", value = "/page-count")
|
||||||
public Boolean pageCount(
|
@Operation(summary = "Checks if a PDF is greater, less or equal to a setPageCount", description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
public Boolean pageCount(
|
||||||
@Parameter(description = "Page Count", required = true) String pageCount,
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
||||||
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator)
|
@Parameter(description = "Page Count", required = true) String pageCount,
|
||||||
throws IOException, InterruptedException {
|
@Parameter(description = "Comparison type",
|
||||||
// Load the PDF
|
schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
allowableValues = {"Greater", "Equal", "Less"})) String comparator)
|
||||||
int actualPageCount = document.getNumberOfPages();
|
throws IOException, InterruptedException {
|
||||||
|
// Load the PDF
|
||||||
// Perform the comparison
|
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
||||||
switch (comparator) {
|
int actualPageCount = document.getNumberOfPages();
|
||||||
case "Greater":
|
|
||||||
return actualPageCount > Integer.parseInt(pageCount);
|
// Perform the comparison
|
||||||
case "Equal":
|
switch (comparator) {
|
||||||
return actualPageCount == Integer.parseInt(pageCount);
|
case "Greater":
|
||||||
case "Less":
|
return actualPageCount > Integer.parseInt(pageCount);
|
||||||
return actualPageCount < Integer.parseInt(pageCount);
|
case "Equal":
|
||||||
default:
|
return actualPageCount == Integer.parseInt(pageCount);
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
case "Less":
|
||||||
}
|
return actualPageCount < Integer.parseInt(pageCount);
|
||||||
}
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/page-size")
|
}
|
||||||
@Operation(summary = "Checks if a PDF is of a certain size", description = "Input:PDF Output:Boolean Type:SISO")
|
}
|
||||||
public Boolean pageSize(
|
|
||||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
@PostMapping(consumes = "multipart/form-data", value = "/page-size")
|
||||||
@Parameter(description = "Standard Page Size", required = true) String standardPageSize,
|
@Operation(summary = "Checks if a PDF is of a certain size", description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator)
|
public Boolean pageSize(
|
||||||
throws IOException, InterruptedException {
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
||||||
|
@Parameter(description = "Standard Page Size", required = true) String standardPageSize,
|
||||||
// Load the PDF
|
@Parameter(description = "Comparison type",
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
|
||||||
|
allowableValues = {"Greater", "Equal", "Less"})) String comparator)
|
||||||
PDPage firstPage = document.getPage(0);
|
throws IOException, InterruptedException {
|
||||||
PDRectangle actualPageSize = firstPage.getMediaBox();
|
|
||||||
|
// Load the PDF
|
||||||
// Calculate the area of the actual page size
|
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
||||||
float actualArea = actualPageSize.getWidth() * actualPageSize.getHeight();
|
|
||||||
|
PDPage firstPage = document.getPage(0);
|
||||||
// Get the standard size and calculate its area
|
PDRectangle actualPageSize = firstPage.getMediaBox();
|
||||||
PDRectangle standardSize = PdfUtils.textToPageSize(standardPageSize);
|
|
||||||
float standardArea = standardSize.getWidth() * standardSize.getHeight();
|
// Calculate the area of the actual page size
|
||||||
|
float actualArea = actualPageSize.getWidth() * actualPageSize.getHeight();
|
||||||
// Perform the comparison
|
|
||||||
switch (comparator) {
|
// Get the standard size and calculate its area
|
||||||
case "Greater":
|
PDRectangle standardSize = PdfUtils.textToPageSize(standardPageSize);
|
||||||
return actualArea > standardArea;
|
float standardArea = standardSize.getWidth() * standardSize.getHeight();
|
||||||
case "Equal":
|
|
||||||
return actualArea == standardArea;
|
// Perform the comparison
|
||||||
case "Less":
|
switch (comparator) {
|
||||||
return actualArea < standardArea;
|
case "Greater":
|
||||||
default:
|
return actualArea > standardArea;
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
case "Equal":
|
||||||
}
|
return actualArea == standardArea;
|
||||||
}
|
case "Less":
|
||||||
|
return actualArea < standardArea;
|
||||||
|
default:
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/file-size")
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
@Operation(summary = "Checks if a PDF is a set file size", description = "Input:PDF Output:Boolean Type:SISO")
|
}
|
||||||
public Boolean fileSize(
|
}
|
||||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
|
||||||
@Parameter(description = "File Size", required = true) String fileSize,
|
|
||||||
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator)
|
@PostMapping(consumes = "multipart/form-data", value = "/file-size")
|
||||||
throws IOException, InterruptedException {
|
@Operation(summary = "Checks if a PDF is a set file size", description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
|
public Boolean fileSize(
|
||||||
// Get the file size
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
||||||
long actualFileSize = inputFile.getSize();
|
@Parameter(description = "File Size", required = true) String fileSize,
|
||||||
|
@Parameter(description = "Comparison type",
|
||||||
// Perform the comparison
|
schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
|
||||||
switch (comparator) {
|
allowableValues = {"Greater", "Equal", "Less"})) String comparator)
|
||||||
case "Greater":
|
throws IOException, InterruptedException {
|
||||||
return actualFileSize > Long.parseLong(fileSize);
|
|
||||||
case "Equal":
|
// Get the file size
|
||||||
return actualFileSize == Long.parseLong(fileSize);
|
long actualFileSize = inputFile.getSize();
|
||||||
case "Less":
|
|
||||||
return actualFileSize < Long.parseLong(fileSize);
|
// Perform the comparison
|
||||||
default:
|
switch (comparator) {
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
case "Greater":
|
||||||
}
|
return actualFileSize > Long.parseLong(fileSize);
|
||||||
}
|
case "Equal":
|
||||||
|
return actualFileSize == Long.parseLong(fileSize);
|
||||||
|
case "Less":
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/page-rotation")
|
return actualFileSize < Long.parseLong(fileSize);
|
||||||
@Operation(summary = "Checks if a PDF is of a certain rotation", description = "Input:PDF Output:Boolean Type:SISO")
|
default:
|
||||||
public Boolean pageRotation(
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
}
|
||||||
@Parameter(description = "Rotation in degrees", required = true) int rotation,
|
}
|
||||||
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
|
@PostMapping(consumes = "multipart/form-data", value = "/page-rotation")
|
||||||
// Load the PDF
|
@Operation(summary = "Checks if a PDF is of a certain rotation", description = "Input:PDF Output:Boolean Type:SISO")
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
public Boolean pageRotation(
|
||||||
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
|
||||||
// Get the rotation of the first page
|
@Parameter(description = "Rotation in degrees", required = true) int rotation,
|
||||||
PDPage firstPage = document.getPage(0);
|
@Parameter(description = "Comparison type",
|
||||||
int actualRotation = firstPage.getRotation();
|
schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
|
||||||
|
allowableValues = {"Greater", "Equal", "Less"})) String comparator)
|
||||||
// Perform the comparison
|
throws IOException, InterruptedException {
|
||||||
switch (comparator) {
|
|
||||||
case "Greater":
|
// Load the PDF
|
||||||
return actualRotation > rotation;
|
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
||||||
case "Equal":
|
|
||||||
return actualRotation == rotation;
|
// Get the rotation of the first page
|
||||||
case "Less":
|
PDPage firstPage = document.getPage(0);
|
||||||
return actualRotation < rotation;
|
int actualRotation = firstPage.getRotation();
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
// Perform the comparison
|
||||||
}
|
switch (comparator) {
|
||||||
}
|
case "Greater":
|
||||||
|
return actualRotation > rotation;
|
||||||
}
|
case "Equal":
|
||||||
|
return actualRotation == rotation;
|
||||||
|
case "Less":
|
||||||
|
return actualRotation < rotation;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid comparator: " + comparator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ import com.google.zxing.Result;
|
||||||
import com.google.zxing.common.HybridBinarizer;
|
import com.google.zxing.common.HybridBinarizer;
|
||||||
|
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class AutoSplitPdfController {
|
public class AutoSplitPdfController {
|
||||||
|
@ -37,7 +39,10 @@ public class AutoSplitPdfController {
|
||||||
private static final String QR_CONTENT = "https://github.com/Frooodle/Stirling-PDF";
|
private static final String QR_CONTENT = "https://github.com/Frooodle/Stirling-PDF";
|
||||||
|
|
||||||
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
|
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
|
||||||
public ResponseEntity<byte[]> autoSplitPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
|
@Operation(summary = "Auto split PDF pages into separate documents", description = "This endpoint accepts a PDF file, scans each page for a specific QR code, and splits the document at the QR code boundaries. The output is a zip file containing each separate PDF document. Input:PDF Output:ZIP Type:SISO")
|
||||||
|
public ResponseEntity<byte[]> autoSplitPdf(
|
||||||
|
@RequestParam("fileInput") @Parameter(description = "The input PDF file which needs to be split into separate documents based on QR code boundaries.", required = true) MultipartFile file)
|
||||||
|
throws IOException {
|
||||||
InputStream inputStream = file.getInputStream();
|
InputStream inputStream = file.getInputStream();
|
||||||
PDDocument document = PDDocument.load(inputStream);
|
PDDocument document = PDDocument.load(inputStream);
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
|
|
|
@ -53,9 +53,9 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
||||||
public class Controller {
|
public class PipelineController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Controller.class);
|
private static final Logger logger = LoggerFactory.getLogger(PipelineController.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@ -246,11 +246,11 @@ public class Controller {
|
||||||
|
|
||||||
List<Resource> processFiles(List<Resource> outputFiles, String jsonString) throws Exception {
|
List<Resource> processFiles(List<Resource> outputFiles, String jsonString) throws Exception {
|
||||||
|
|
||||||
logger.info("Processing files... " + outputFiles);
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
JsonNode jsonNode = mapper.readTree(jsonString);
|
JsonNode jsonNode = mapper.readTree(jsonString);
|
||||||
|
|
||||||
JsonNode pipelineNode = jsonNode.get("pipeline");
|
JsonNode pipelineNode = jsonNode.get("pipeline");
|
||||||
|
logger.info("Running pipelineNode: {}", pipelineNode);
|
||||||
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
||||||
PrintStream logPrintStream = new PrintStream(logStream);
|
PrintStream logPrintStream = new PrintStream(logStream);
|
||||||
|
|
||||||
|
@ -298,19 +298,32 @@ public class Controller {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the response body is a zip file
|
|
||||||
if (isZip(response.getBody())) {
|
// Define filename
|
||||||
// Unzip the file and add all the files to the new output files
|
String filename;
|
||||||
newOutputFiles.addAll(unzip(response.getBody()));
|
if ("auto-rename".equals(operation)) {
|
||||||
} else {
|
// If the operation is "auto-rename", generate a new filename.
|
||||||
Resource outputResource = new ByteArrayResource(response.getBody()) {
|
// This is a simple example of generating a filename using current timestamp.
|
||||||
@Override
|
// Modify as per your needs.
|
||||||
public String getFilename() {
|
filename = "file_" + System.currentTimeMillis();
|
||||||
return file.getFilename(); // Preserving original filename
|
} else {
|
||||||
}
|
// Otherwise, keep the original filename.
|
||||||
};
|
filename = file.getFilename();
|
||||||
newOutputFiles.add(outputResource);
|
}
|
||||||
}
|
|
||||||
|
// Check if the response body is a zip file
|
||||||
|
if (isZip(response.getBody())) {
|
||||||
|
// Unzip the file and add all the files to the new output files
|
||||||
|
newOutputFiles.addAll(unzip(response.getBody()));
|
||||||
|
} else {
|
||||||
|
Resource outputResource = new ByteArrayResource(response.getBody()) {
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
newOutputFiles.add(outputResource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasInputFileType) {
|
if (!hasInputFileType) {
|
|
@ -47,6 +47,11 @@ public class WatermarkController {
|
||||||
@RequestPart(required = true) @Parameter(description = "The watermark type (text or image)") String watermarkType,
|
@RequestPart(required = true) @Parameter(description = "The watermark type (text or image)") String watermarkType,
|
||||||
@RequestPart(required = false) @Parameter(description = "The watermark text") String watermarkText,
|
@RequestPart(required = false) @Parameter(description = "The watermark text") String watermarkText,
|
||||||
@RequestPart(required = false) @Parameter(description = "The watermark image") MultipartFile watermarkImage,
|
@RequestPart(required = false) @Parameter(description = "The watermark image") MultipartFile watermarkImage,
|
||||||
|
|
||||||
|
@RequestParam(defaultValue = "roman", name = "alphabet") @Parameter(description = "The selected alphabet",
|
||||||
|
schema = @Schema(type = "string",
|
||||||
|
allowableValues = {"roman","arabic","japanese","korean","chinese"},
|
||||||
|
defaultValue = "roman")) String alphabet,
|
||||||
@RequestParam(defaultValue = "30", name = "fontSize") @Parameter(description = "The font size of the watermark text", example = "30") float fontSize,
|
@RequestParam(defaultValue = "30", name = "fontSize") @Parameter(description = "The font size of the watermark text", example = "30") float fontSize,
|
||||||
@RequestParam(defaultValue = "0", name = "rotation") @Parameter(description = "The rotation of the watermark in degrees", example = "0") float rotation,
|
@RequestParam(defaultValue = "0", name = "rotation") @Parameter(description = "The rotation of the watermark in degrees", example = "0") float rotation,
|
||||||
@RequestParam(defaultValue = "0.5", name = "opacity") @Parameter(description = "The opacity of the watermark (0.0 - 1.0)", example = "0.5") float opacity,
|
@RequestParam(defaultValue = "0.5", name = "opacity") @Parameter(description = "The opacity of the watermark (0.0 - 1.0)", example = "0.5") float opacity,
|
||||||
|
@ -71,7 +76,7 @@ public class WatermarkController {
|
||||||
|
|
||||||
if (watermarkType.equalsIgnoreCase("text")) {
|
if (watermarkType.equalsIgnoreCase("text")) {
|
||||||
addTextWatermark(contentStream, watermarkText, document, page, rotation, widthSpacer, heightSpacer,
|
addTextWatermark(contentStream, watermarkText, document, page, rotation, widthSpacer, heightSpacer,
|
||||||
fontSize);
|
fontSize, alphabet);
|
||||||
} else if (watermarkType.equalsIgnoreCase("image")) {
|
} else if (watermarkType.equalsIgnoreCase("image")) {
|
||||||
addImageWatermark(contentStream, watermarkImage, document, page, rotation, widthSpacer, heightSpacer,
|
addImageWatermark(contentStream, watermarkImage, document, page, rotation, widthSpacer, heightSpacer,
|
||||||
fontSize);
|
fontSize);
|
||||||
|
@ -86,9 +91,41 @@ public class WatermarkController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTextWatermark(PDPageContentStream contentStream, String watermarkText, PDDocument document,
|
private void addTextWatermark(PDPageContentStream contentStream, String watermarkText, PDDocument document,
|
||||||
PDPage page, float rotation, int widthSpacer, int heightSpacer, float fontSize) throws IOException {
|
PDPage page, float rotation, int widthSpacer, int heightSpacer, float fontSize, String alphabet) throws IOException {
|
||||||
// Set font and other properties for text watermark
|
String resourceDir = "";
|
||||||
PDFont font = PDType1Font.HELVETICA_BOLD;
|
PDFont font = PDType1Font.HELVETICA_BOLD;
|
||||||
|
switch (alphabet) {
|
||||||
|
case "arabic":
|
||||||
|
resourceDir = "static/fonts/NotoSansArabic-Regular.ttf";
|
||||||
|
break;
|
||||||
|
case "japanese":
|
||||||
|
resourceDir = "static/fonts/Meiryo.ttf";
|
||||||
|
break;
|
||||||
|
case "korean":
|
||||||
|
resourceDir = "static/fonts/malgun.ttf";
|
||||||
|
break;
|
||||||
|
case "chinese":
|
||||||
|
resourceDir = "static/fonts/SimSun.ttf";
|
||||||
|
break;
|
||||||
|
case "roman":
|
||||||
|
default:
|
||||||
|
resourceDir = "static/fonts/NotoSans-Regular.ttf";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!resourceDir.equals("")) {
|
||||||
|
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
||||||
|
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
||||||
|
File tempFile = File.createTempFile("NotoSansFont", fileExtension);
|
||||||
|
try (InputStream is = classPathResource.getInputStream(); FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||||
|
IOUtils.copy(is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
font = PDType0Font.load(document, tempFile);
|
||||||
|
tempFile.deleteOnExit();
|
||||||
|
}
|
||||||
|
|
||||||
contentStream.setFont(font, fontSize);
|
contentStream.setFont(font, fontSize);
|
||||||
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class PdfUtils {
|
||||||
|
|
||||||
|
|
||||||
public static PDRectangle textToPageSize(String size) {
|
public static PDRectangle textToPageSize(String size) {
|
||||||
switch (size) {
|
switch (size.toUpperCase()) {
|
||||||
case "A0":
|
case "A0":
|
||||||
return PDRectangle.A0;
|
return PDRectangle.A0;
|
||||||
case "A1":
|
case "A1":
|
||||||
|
|
|
@ -87,7 +87,7 @@ document.getElementById('submitConfigBtn').addEventListener('click', function()
|
||||||
|
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
|
|
||||||
let fileInput = document.getElementById('fileInput');
|
let fileInput = document.getElementById('fileInput-input');
|
||||||
let files = fileInput.files;
|
let files = fileInput.files;
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
@ -177,7 +177,11 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||||
let listItem = document.createElement('li');
|
let listItem = document.createElement('li');
|
||||||
listItem.className = "list-group-item";
|
listItem.className = "list-group-item";
|
||||||
let hasSettings = (apiDocs[selectedOperation] && apiDocs[selectedOperation].post &&
|
let hasSettings = (apiDocs[selectedOperation] && apiDocs[selectedOperation].post &&
|
||||||
apiDocs[selectedOperation].post.parameters && apiDocs[selectedOperation].post.parameters.length > 0);
|
((apiDocs[selectedOperation].post.parameters && apiDocs[selectedOperation].post.parameters.length > 0) ||
|
||||||
|
(apiDocs[selectedOperation].post.requestBody &&
|
||||||
|
apiDocs[selectedOperation].post.requestBody.content['multipart/form-data'].schema.properties)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
listItem.innerHTML = `
|
listItem.innerHTML = `
|
||||||
|
@ -222,52 +226,77 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||||
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
|
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
|
||||||
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
|
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
|
||||||
let operationData = apiDocs[operation].post.parameters || [];
|
let operationData = apiDocs[operation].post.parameters || [];
|
||||||
|
let requestBodyData = apiDocs[operation].post.requestBody.content['multipart/form-data'].schema.properties || {};
|
||||||
|
|
||||||
|
// Combine operationData and requestBodyData into a single array
|
||||||
|
operationData = operationData.concat(Object.keys(requestBodyData).map(key => ({
|
||||||
|
name: key,
|
||||||
|
schema: requestBodyData[key]
|
||||||
|
})));
|
||||||
|
|
||||||
pipelineSettingsContent.innerHTML = '';
|
pipelineSettingsContent.innerHTML = '';
|
||||||
|
|
||||||
operationData.forEach(parameter => {
|
operationData.forEach(parameter => {
|
||||||
let parameterDiv = document.createElement('div');
|
let parameterDiv = document.createElement('div');
|
||||||
parameterDiv.className = "form-group";
|
parameterDiv.className = "form-group";
|
||||||
|
|
||||||
let parameterLabel = document.createElement('label');
|
let parameterLabel = document.createElement('label');
|
||||||
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
|
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
|
||||||
parameterLabel.title = parameter.description;
|
parameterLabel.title = parameter.description;
|
||||||
parameterDiv.appendChild(parameterLabel);
|
parameterDiv.appendChild(parameterLabel);
|
||||||
|
|
||||||
let parameterInput;
|
let parameterInput;
|
||||||
switch (parameter.schema.type) {
|
|
||||||
case 'string':
|
// check if enum exists in schema
|
||||||
case 'number':
|
if (parameter.schema.enum) {
|
||||||
case 'integer':
|
// if enum exists, create a select element
|
||||||
parameterInput = document.createElement('input');
|
parameterInput = document.createElement('select');
|
||||||
parameterInput.type = parameter.schema.type === 'string' ? 'text' : 'number';
|
parameterInput.className = "form-control";
|
||||||
parameterInput.className = "form-control";
|
|
||||||
break;
|
// iterate over each enum value and create an option for it
|
||||||
case 'boolean':
|
parameter.schema.enum.forEach(value => {
|
||||||
parameterInput = document.createElement('input');
|
let option = document.createElement('option');
|
||||||
parameterInput.type = 'checkbox';
|
option.value = value;
|
||||||
break;
|
option.text = value;
|
||||||
case 'array':
|
parameterInput.appendChild(option);
|
||||||
case 'object':
|
});
|
||||||
parameterInput = document.createElement('textarea');
|
} else {
|
||||||
parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}`;
|
// switch-case statement for handling non-enum types
|
||||||
parameterInput.className = "form-control";
|
switch (parameter.schema.type) {
|
||||||
break;
|
case 'string':
|
||||||
case 'enum':
|
if (parameter.schema.format === 'binary') {
|
||||||
parameterInput = document.createElement('select');
|
// This is a file input
|
||||||
parameterInput.className = "form-control";
|
parameterInput = document.createElement('input');
|
||||||
parameter.schema.enum.forEach(option => {
|
parameterInput.type = 'file';
|
||||||
let optionElement = document.createElement('option');
|
parameterInput.className = "form-control";
|
||||||
optionElement.value = option;
|
} else {
|
||||||
optionElement.text = option;
|
parameterInput = document.createElement('input');
|
||||||
parameterInput.appendChild(optionElement);
|
parameterInput.type = 'text';
|
||||||
});
|
parameterInput.className = "form-control";
|
||||||
break;
|
}
|
||||||
default:
|
break;
|
||||||
parameterInput = document.createElement('input');
|
case 'number':
|
||||||
parameterInput.type = 'text';
|
case 'integer':
|
||||||
parameterInput.className = "form-control";
|
parameterInput = document.createElement('input');
|
||||||
}
|
parameterInput.type = 'number';
|
||||||
|
parameterInput.className = "form-control";
|
||||||
|
break;
|
||||||
|
case 'boolean':
|
||||||
|
parameterInput = document.createElement('input');
|
||||||
|
parameterInput.type = 'checkbox';
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
case 'object':
|
||||||
|
parameterInput = document.createElement('textarea');
|
||||||
|
parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}`;
|
||||||
|
parameterInput.className = "form-control";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parameterInput = document.createElement('input');
|
||||||
|
parameterInput.type = 'text';
|
||||||
|
parameterInput.className = "form-control";
|
||||||
|
}
|
||||||
|
}
|
||||||
parameterInput.id = parameter.name;
|
parameterInput.id = parameter.name;
|
||||||
|
|
||||||
if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) {
|
if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) {
|
||||||
|
|
|
@ -3,98 +3,122 @@
|
||||||
th:lang-direction="#{language.direction}"
|
th:lang-direction="#{language.direction}"
|
||||||
xmlns:th="http://www.thymeleaf.org">
|
xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pipeline.title})}"></th:block>
|
<th:block
|
||||||
<body>
|
th:insert="~{fragments/common :: head(title=#{pipeline.title})}"></th:block>
|
||||||
<div id="page-container">
|
<style>
|
||||||
<div id="content-wrap">
|
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
|
||||||
<br> <br>
|
|
||||||
<div class="container" id="dropContainer">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
|
|
||||||
<!-- Trigger/Open The Modal -->
|
|
||||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#pipelineSettingsModal">
|
|
||||||
Open Pipeline Settings
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
<button id="uploadPipelineBtn" class="btn btn-primary">Upload Custom Pipeline</button>
|
|
||||||
<select id="pipelineSelect">
|
|
||||||
<option value="">Select a pipeline</option>
|
|
||||||
<th:block th:each="config : ${pipelineConfigsWithNames}">
|
|
||||||
<option th:value="${config.json}" th:text="${config.name}"></option>
|
|
||||||
</th:block>
|
|
||||||
|
|
||||||
</select>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input type="file" id="fileInput" multiple>
|
|
||||||
<button class="btn btn-primary" id="submitConfigBtn">Submit</button>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- The Modal -->
|
|
||||||
<div class="modal" id="pipelineSettingsModal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
|
|
||||||
<!-- Modal Header -->
|
|
||||||
<div class="modal-header">
|
|
||||||
<h2 class="modal-title">Pipeline Configuration</h2>
|
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal body -->
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="pipelineName" class="form-label">Pipeline Name</label>
|
|
||||||
<input type="text" id="pipelineName" class="form-control" placeholder="Enter pipeline name here">
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<select id="operationsDropdown" class="form-select">
|
|
||||||
<!-- Options will be dynamically populated here -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<button id="addOperationBtn" class="btn btn-primary">Add operation</button>
|
|
||||||
</div>
|
|
||||||
<h3>Pipeline:</h3>
|
|
||||||
<ol id="pipelineList" class="list-group">
|
|
||||||
<!-- Pipeline operations will be dynamically populated here -->
|
|
||||||
</ol>
|
|
||||||
<div id="pipelineSettingsContent">
|
|
||||||
<!-- pipelineSettings will be dynamically populated here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal footer -->
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button id="savePipelineBtn" class="btn btn-success">Download</button>
|
|
||||||
<button id="validateButton" class="btn btn-success">Validate</button>
|
|
||||||
<div class="btn-group">
|
|
||||||
<input type="file" id="uploadPipelineInput" accept=".json" style="display: none;">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="js/pipeline.js"></script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
|
|
||||||
|
|
||||||
.btn-margin {
|
.btn-margin {
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bordered-box {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-element {
|
||||||
|
width: 80%;
|
||||||
|
text-align: center;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.element-margin {
|
||||||
|
margin: 10px 0; /* Adjust this value to increase/decrease the margin as needed */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
|
||||||
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
|
||||||
|
<div class="bordered-box">
|
||||||
|
<div class="text-right text-top">
|
||||||
|
<button id="uploadPipelineBtn" class="btn btn-primary">Upload
|
||||||
|
Custom</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-toggle="modal"
|
||||||
|
data-target="#pipelineSettingsModal">Configure</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="center-element">
|
||||||
|
<div class="element-margin">
|
||||||
|
<select id="pipelineSelect" class="custom-select">
|
||||||
|
<option value="">Select a pipeline</option>
|
||||||
|
<th:block th:each="config : ${pipelineConfigsWithNames}">
|
||||||
|
<option th:value="${config.json}" th:text="${config.name}"></option>
|
||||||
|
</th:block>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="element-margin">
|
||||||
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=true)}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="element-margin">
|
||||||
|
<button class="btn btn-primary" id="submitConfigBtn">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- The Modal -->
|
||||||
|
<div class="modal" id="pipelineSettingsModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
|
||||||
|
<!-- Modal Header -->
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-title">Pipeline Configuration</h2>
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal body -->
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="pipelineName" class="form-label">Pipeline
|
||||||
|
Name</label> <input type="text" id="pipelineName"
|
||||||
|
class="form-control" placeholder="Enter pipeline name here">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<select id="operationsDropdown" class="form-select">
|
||||||
|
<!-- Options will be dynamically populated here -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<button id="addOperationBtn" class="btn btn-primary">Add
|
||||||
|
operation</button>
|
||||||
|
</div>
|
||||||
|
<h3>Pipeline:</h3>
|
||||||
|
<ol id="pipelineList" class="list-group">
|
||||||
|
<!-- Pipeline operations will be dynamically populated here -->
|
||||||
|
</ol>
|
||||||
|
<div id="pipelineSettingsContent">
|
||||||
|
<!-- pipelineSettings will be dynamically populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal footer -->
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="savePipelineBtn" class="btn btn-success">Download</button>
|
||||||
|
<button id="validateButton" class="btn btn-success">Validate</button>
|
||||||
|
<div class="btn-group">
|
||||||
|
<input type="file" id="uploadPipelineInput" accept=".json"
|
||||||
|
style="display: none;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/pipeline.js"></script>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue