itext changes
This commit is contained in:
parent
648df7b3d3
commit
a7cd6bfd2e
12 changed files with 668 additions and 497 deletions
|
@ -86,6 +86,7 @@ dependencies {
|
||||||
|
|
||||||
//general PDF
|
//general PDF
|
||||||
implementation 'org.apache.pdfbox:pdfbox:2.0.29'
|
implementation 'org.apache.pdfbox:pdfbox:2.0.29'
|
||||||
|
implementation 'org.apache.pdfbox:xmpbox:2.0.29'
|
||||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
|
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
|
||||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
|
implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
|
||||||
implementation 'com.itextpdf:itext7-core:7.2.5'
|
implementation 'com.itextpdf:itext7-core:7.2.5'
|
||||||
|
|
|
@ -66,7 +66,6 @@ public class SPdfApplication {
|
||||||
|
|
||||||
GeneralUtils.createDir("customFiles/static/");
|
GeneralUtils.createDir("customFiles/static/");
|
||||||
GeneralUtils.createDir("customFiles/templates/");
|
GeneralUtils.createDir("customFiles/templates/");
|
||||||
GeneralUtils.createDir("config");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,9 @@ public class OpenApiConfig {
|
||||||
public OpenAPI customOpenAPI() {
|
public OpenAPI customOpenAPI() {
|
||||||
String version = getClass().getPackage().getImplementationVersion();
|
String version = getClass().getPackage().getImplementationVersion();
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
Properties props = new Properties();
|
|
||||||
try (InputStream input = getClass().getClassLoader().getResourceAsStream("version.properties")) {
|
|
||||||
props.load(input);
|
|
||||||
version = props.getProperty("version");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
version = "1.0.0"; // default version if all else fails
|
version = "1.0.0"; // default version if all else fails
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OpenAPI().components(new Components()).info(
|
return new OpenAPI().components(new Components()).info(
|
||||||
|
|
|
@ -14,25 +14,26 @@ import jakarta.annotation.PostConstruct;
|
||||||
import stirling.software.SPDF.config.security.UserService;
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
|
import stirling.software.SPDF.model.*;
|
||||||
@Component
|
@Component
|
||||||
public class InitialSecuritySetup {
|
public class InitialSecuritySetup {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
ApplicationProperties applicationProperties;
|
ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
if (!userService.hasUsers()) {
|
if (!userService.hasUsers()) {
|
||||||
String initialUsername = applicationProperties.getSecurity().getInitialLogin().getUsername();
|
String initialUsername = "admin";
|
||||||
String initialPassword = applicationProperties.getSecurity().getInitialLogin().getPassword();
|
String initialPassword = "stirling";
|
||||||
if (initialUsername != null && initialPassword != null) {
|
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||||
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
package stirling.software.SPDF.controller.api;
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
@ -24,6 +30,8 @@ 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.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.multipdf.LayerUtility;
|
||||||
@RestController
|
@RestController
|
||||||
@Tag(name = "General", description = "General APIs")
|
@Tag(name = "General", description = "General APIs")
|
||||||
public class ToSinglePageController {
|
public class ToSinglePageController {
|
||||||
|
@ -41,37 +49,34 @@ public class ToSinglePageController {
|
||||||
@Parameter(description = "The input multi-page PDF file to be converted into a single page", required = true)
|
@Parameter(description = "The input multi-page PDF file to be converted into a single page", required = true)
|
||||||
MultipartFile file) throws IOException {
|
MultipartFile file) throws IOException {
|
||||||
|
|
||||||
PdfReader reader = new PdfReader(file.getInputStream());
|
PDDocument sourceDocument = PDDocument.load(file.getInputStream());
|
||||||
PdfDocument sourceDocument = new PdfDocument(reader);
|
|
||||||
|
|
||||||
float totalHeight = 0;
|
float totalHeight = 0;
|
||||||
float width = 0;
|
float width = 0;
|
||||||
|
|
||||||
for (int i = 1; i <= sourceDocument.getNumberOfPages(); i++) {
|
for (PDPage page : sourceDocument.getPages()) {
|
||||||
Rectangle pageSize = sourceDocument.getPage(i).getPageSize();
|
PDRectangle pageSize = page.getMediaBox();
|
||||||
totalHeight += pageSize.getHeight();
|
totalHeight += pageSize.getHeight();
|
||||||
if(width < pageSize.getWidth())
|
if(width < pageSize.getWidth())
|
||||||
width = pageSize.getWidth();
|
width = pageSize.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
PDDocument newDocument = new PDDocument();
|
||||||
|
PDPage newPage = new PDPage(new PDRectangle(width, totalHeight));
|
||||||
|
newDocument.addPage(newPage);
|
||||||
|
|
||||||
|
LayerUtility layerUtility = new LayerUtility(newDocument);
|
||||||
|
float yOffset = totalHeight;
|
||||||
|
|
||||||
|
for (PDPage page : sourceDocument.getPages()) {
|
||||||
|
PDFormXObject form = layerUtility.importPageAsForm(sourceDocument, sourceDocument.getPages().indexOf(page));
|
||||||
|
AffineTransform af = AffineTransform.getTranslateInstance(0, yOffset - page.getMediaBox().getHeight());
|
||||||
|
layerUtility.appendFormAsLayer(newDocument.getPage(0), form, af, page.getResources().getCOSObject().toString());
|
||||||
|
yOffset -= page.getMediaBox().getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
PdfWriter writer = new PdfWriter(baos);
|
newDocument.save(baos);
|
||||||
PdfDocument newDocument = new PdfDocument(writer);
|
newDocument.close();
|
||||||
PageSize newPageSize = new PageSize(width, totalHeight);
|
|
||||||
newDocument.addNewPage(newPageSize);
|
|
||||||
|
|
||||||
Document layoutDoc = new Document(newDocument);
|
|
||||||
float yOffset = totalHeight;
|
|
||||||
|
|
||||||
for (int i = 1; i <= sourceDocument.getNumberOfPages(); i++) {
|
|
||||||
PdfFormXObject pageCopy = sourceDocument.getPage(i).copyAsFormXObject(newDocument);
|
|
||||||
Image copiedPage = new Image(pageCopy);
|
|
||||||
copiedPage.setFixedPosition(0, yOffset - sourceDocument.getPage(i).getPageSize().getHeight());
|
|
||||||
yOffset -= sourceDocument.getPage(i).getPageSize().getHeight();
|
|
||||||
layoutDoc.add(copiedPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
layoutDoc.close();
|
|
||||||
sourceDocument.close();
|
sourceDocument.close();
|
||||||
|
|
||||||
byte[] result = baos.toByteArray();
|
byte[] result = baos.toByteArray();
|
||||||
|
|
|
@ -6,6 +6,11 @@ import java.io.IOException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
@ -15,18 +20,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import com.itextpdf.io.font.constants.StandardFonts;
|
|
||||||
import com.itextpdf.kernel.font.PdfFont;
|
|
||||||
import com.itextpdf.kernel.font.PdfFontFactory;
|
|
||||||
import com.itextpdf.kernel.geom.Rectangle;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfDocument;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfPage;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfReader;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfWriter;
|
|
||||||
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
|
|
||||||
import com.itextpdf.layout.Canvas;
|
|
||||||
import com.itextpdf.layout.element.Paragraph;
|
|
||||||
import com.itextpdf.layout.properties.TextAlignment;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -51,11 +45,10 @@ public class PageNumbersController {
|
||||||
@Parameter(description = "Which pages to number, default all", required = false, schema = @Schema(type = "string")) @RequestParam(value = "pagesToNumber", required = false) String pagesToNumber,
|
@Parameter(description = "Which pages to number, default all", required = false, schema = @Schema(type = "string")) @RequestParam(value = "pagesToNumber", required = false) String pagesToNumber,
|
||||||
@Parameter(description = "Custom text: defaults to just number but can have things like \"Page {n} of {p}\"", required = false, schema = @Schema(type = "string")) @RequestParam(value = "customText", required = false) String customText)
|
@Parameter(description = "Custom text: defaults to just number but can have things like \"Page {n} of {p}\"", required = false, schema = @Schema(type = "string")) @RequestParam(value = "customText", required = false) String customText)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
int pageNumber = startingNumber;
|
||||||
|
byte[] fileBytes = file.getBytes();
|
||||||
|
PDDocument document = PDDocument.load(fileBytes);
|
||||||
|
|
||||||
byte[] fileBytes = file.getBytes();
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
|
|
||||||
|
|
||||||
int pageNumber = startingNumber;
|
|
||||||
float marginFactor;
|
float marginFactor;
|
||||||
switch (customMargin.toLowerCase()) {
|
switch (customMargin.toLowerCase()) {
|
||||||
case "small":
|
case "small":
|
||||||
|
@ -67,79 +60,67 @@ public class PageNumbersController {
|
||||||
case "large":
|
case "large":
|
||||||
marginFactor = 0.05f;
|
marginFactor = 0.05f;
|
||||||
break;
|
break;
|
||||||
case "x-large":
|
|
||||||
marginFactor = 0.1f;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
marginFactor = 0.035f;
|
marginFactor = 0.035f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
float fontSize = 12.0f;
|
float fontSize = 12.0f;
|
||||||
|
PDType1Font font = PDType1Font.HELVETICA;
|
||||||
|
|
||||||
PdfReader reader = new PdfReader(bais);
|
List<Integer> pagesToNumberList = GeneralUtils.parsePageList(pagesToNumber.split(","), document.getNumberOfPages());
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
PdfWriter writer = new PdfWriter(baos);
|
|
||||||
|
|
||||||
PdfDocument pdfDoc = new PdfDocument(reader, writer);
|
|
||||||
|
|
||||||
List<Integer> pagesToNumberList = GeneralUtils.parsePageList(pagesToNumber.split(","), pdfDoc.getNumberOfPages());
|
|
||||||
|
|
||||||
for (int i : pagesToNumberList) {
|
for (int i : pagesToNumberList) {
|
||||||
PdfPage page = pdfDoc.getPage(i+1);
|
PDPage page = document.getPage(i);
|
||||||
Rectangle pageSize = page.getPageSize();
|
PDRectangle pageSize = page.getMediaBox();
|
||||||
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc);
|
|
||||||
|
|
||||||
String text = customText != null ? customText.replace("{n}", String.valueOf(pageNumber)).replace("{total}", String.valueOf(pdfDoc.getNumberOfPages())).replace("{filename}", file.getOriginalFilename().replaceFirst("[.][^.]+$", "")) : String.valueOf(pageNumber);
|
String text = customText != null ? customText.replace("{n}", String.valueOf(pageNumber)).replace("{total}", String.valueOf(document.getNumberOfPages())).replace("{filename}", file.getOriginalFilename().replaceFirst("[.][^.]+$", "")) : String.valueOf(pageNumber);
|
||||||
|
|
||||||
PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA);
|
|
||||||
float textWidth = font.getWidth(text, fontSize);
|
|
||||||
float textHeight = font.getAscent(text, fontSize) - font.getDescent(text, fontSize);
|
|
||||||
|
|
||||||
float x, y;
|
float x, y;
|
||||||
TextAlignment alignment;
|
|
||||||
|
|
||||||
int xGroup = (position - 1) % 3;
|
int xGroup = (position - 1) % 3;
|
||||||
int yGroup = 2 - (position - 1) / 3;
|
int yGroup = 2 - (position - 1) / 3;
|
||||||
|
|
||||||
switch (xGroup) {
|
switch (xGroup) {
|
||||||
case 0: // left
|
case 0: // left
|
||||||
x = pageSize.getLeft() + marginFactor * pageSize.getWidth();
|
x = pageSize.getLowerLeftX() + marginFactor * pageSize.getWidth();
|
||||||
alignment = TextAlignment.LEFT;
|
|
||||||
break;
|
break;
|
||||||
case 1: // center
|
case 1: // center
|
||||||
x = pageSize.getLeft() + (pageSize.getWidth()) / 2;
|
x = pageSize.getLowerLeftX() + (pageSize.getWidth() / 2);
|
||||||
alignment = TextAlignment.CENTER;
|
|
||||||
break;
|
break;
|
||||||
default: // right
|
default: // right
|
||||||
x = pageSize.getRight() - marginFactor * pageSize.getWidth();
|
x = pageSize.getUpperRightX() - marginFactor * pageSize.getWidth();
|
||||||
alignment = TextAlignment.RIGHT;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (yGroup) {
|
switch (yGroup) {
|
||||||
case 0: // bottom
|
case 0: // bottom
|
||||||
y = pageSize.getBottom() + marginFactor * pageSize.getHeight();
|
y = pageSize.getLowerLeftY() + marginFactor * pageSize.getHeight();
|
||||||
break;
|
break;
|
||||||
case 1: // middle
|
case 1: // middle
|
||||||
y = pageSize.getBottom() + (pageSize.getHeight() ) / 2;
|
y = pageSize.getLowerLeftY() + (pageSize.getHeight() / 2);
|
||||||
break;
|
break;
|
||||||
default: // top
|
default: // top
|
||||||
y = pageSize.getTop() - marginFactor * pageSize.getHeight();
|
y = pageSize.getUpperRightY() - marginFactor * pageSize.getHeight();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
new Canvas(pdfCanvas, page.getPageSize())
|
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
|
||||||
.showTextAligned(new Paragraph(text).setFont(font).setFontSize(fontSize), x, y, alignment);
|
contentStream.beginText();
|
||||||
|
contentStream.setFont(font, fontSize);
|
||||||
|
contentStream.newLineAtOffset(x, y);
|
||||||
|
contentStream.showText(text);
|
||||||
|
contentStream.endText();
|
||||||
|
contentStream.close();
|
||||||
|
|
||||||
pageNumber++;
|
pageNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
document.save(baos);
|
||||||
|
document.close();
|
||||||
|
|
||||||
pdfDoc.close();
|
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), URLEncoder.encode(file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf", "UTF-8"), MediaType.APPLICATION_PDF);
|
||||||
byte[] resultBytes = baos.toByteArray();
|
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(resultBytes, URLEncoder.encode(file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf", "UTF-8"), MediaType.APPLICATION_PDF);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
package stirling.software.SPDF.controller.api.other;
|
package stirling.software.SPDF.controller.api.other;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
@ -10,16 +14,6 @@ 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 com.itextpdf.kernel.pdf.PdfArray;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfDictionary;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfDocument;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfName;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfObject;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfReader;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfStream;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
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.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@RestController
|
@RestController
|
||||||
|
@ -28,55 +22,33 @@ public class ShowJavascript {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ShowJavascript.class);
|
private static final Logger logger = LoggerFactory.getLogger(ShowJavascript.class);
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/show-javascript")
|
@PostMapping(consumes = "multipart/form-data", value = "/show-javascript")
|
||||||
@Operation(summary = "Extract header from PDF file", description = "This endpoint accepts a PDF file and attempts to extract its title or header based on heuristics. Input:PDF Output:PDF Type:SISO")
|
|
||||||
public ResponseEntity<byte[]> extractHeader(
|
public ResponseEntity<byte[]> extractHeader(
|
||||||
@RequestPart(value = "fileInput") @Parameter(description = "The input PDF file from which the javascript is to be extracted.", required = true) MultipartFile inputFile)
|
@RequestPart(value = "fileInput") MultipartFile inputFile) throws Exception {
|
||||||
throws Exception {
|
|
||||||
|
String script = "";
|
||||||
|
|
||||||
try (
|
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) {
|
||||||
PdfDocument itextDoc = new PdfDocument(new PdfReader(inputFile.getInputStream()))
|
|
||||||
) {
|
|
||||||
|
|
||||||
String name = "";
|
|
||||||
String script = "";
|
|
||||||
String entryName = "File: "+inputFile.getOriginalFilename() + ", Script: ";
|
|
||||||
//Javascript
|
|
||||||
PdfDictionary namesDict = itextDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Names);
|
|
||||||
if (namesDict != null) {
|
|
||||||
PdfDictionary javascriptDict = namesDict.getAsDictionary(PdfName.JavaScript);
|
|
||||||
if (javascriptDict != null) {
|
|
||||||
|
|
||||||
PdfArray namesArray = javascriptDict.getAsArray(PdfName.Names);
|
PDNameTreeNode<PDActionJavaScript> jsTree = document.getDocumentCatalog().getNames().getJavaScript();
|
||||||
for (int i = 0; i < namesArray.size(); i += 2) {
|
|
||||||
if(namesArray.getAsString(i) != null)
|
|
||||||
name = namesArray.getAsString(i).toString();
|
|
||||||
|
|
||||||
PdfObject jsCode = namesArray.get(i+1);
|
if (jsTree != null) {
|
||||||
if (jsCode instanceof PdfStream) {
|
Map<String, PDActionJavaScript> jsEntries = jsTree.getNames();
|
||||||
byte[] jsCodeBytes = ((PdfStream)jsCode).getBytes();
|
|
||||||
String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
|
|
||||||
script = "//" + entryName + name + "\n" +jsCodeStr;
|
|
||||||
|
|
||||||
} else if (jsCode instanceof PdfDictionary) {
|
for (Map.Entry<String, PDActionJavaScript> entry : jsEntries.entrySet()) {
|
||||||
// If the JS code is in a dictionary, you'll need to know the key to use.
|
String name = entry.getKey();
|
||||||
// Assuming the key is PdfName.JS:
|
PDActionJavaScript jsAction = entry.getValue();
|
||||||
PdfStream jsCodeStream = ((PdfDictionary)jsCode).getAsStream(PdfName.JS);
|
String jsCodeStr = jsAction.getAction();
|
||||||
if (jsCodeStream != null) {
|
|
||||||
byte[] jsCodeBytes = jsCodeStream.getBytes();
|
|
||||||
String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
|
|
||||||
script = "//" + entryName + name + "\n" +jsCodeStr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
script += "// File: " + inputFile.getOriginalFilename() + ", Script: " + name + "\n" + jsCodeStr + "\n";
|
||||||
}
|
}
|
||||||
if(script.equals("")) {
|
}
|
||||||
script = "PDF '" +inputFile.getOriginalFilename() + "' does not contain Javascript";
|
|
||||||
}
|
if (script.isEmpty()) {
|
||||||
return WebResponseUtils.bytesToWebResponse(script.getBytes(), name + ".js");
|
script = "PDF '" + inputFile.getOriginalFilename() + "' does not contain Javascript";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return WebResponseUtils.bytesToWebResponse(script.getBytes(StandardCharsets.UTF_8), inputFile.getOriginalFilename() + ".js");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
package stirling.software.SPDF.controller.api.security;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import org.bouncycastle.cert.jcajce.JcaCertStore;
|
||||||
|
import org.bouncycastle.cms.CMSException;
|
||||||
|
import org.bouncycastle.cms.CMSProcessableByteArray;
|
||||||
|
import org.bouncycastle.cms.CMSSignedData;
|
||||||
|
import org.bouncycastle.cms.CMSSignedDataGenerator;
|
||||||
|
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
|
||||||
|
import org.bouncycastle.operator.ContentSigner;
|
||||||
|
import org.bouncycastle.operator.OperatorCreationException;
|
||||||
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||||
|
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||||
|
import org.bouncycastle.cms.CMSTypedData;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Collections;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ContentDisposition;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.pdfbox.cos.COSDictionary;
|
||||||
|
import org.apache.pdfbox.cos.COSName;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSignDesigner;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
@RestController
|
||||||
|
@Tag(name = "Security", description = "Security APIs")
|
||||||
|
public class CertSignController2 {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(CertSignController2.class);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(consumes = "multipart/form-data", value = "/cert-sign")
|
||||||
|
@Operation(summary = "Sign PDF with a Digital Certificate",
|
||||||
|
description = "This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:MF-SISO")
|
||||||
|
public ResponseEntity<byte[]> signPDF(
|
||||||
|
@RequestPart(required = true, value = "fileInput")
|
||||||
|
@Parameter(description = "The input PDF file to be signed")
|
||||||
|
MultipartFile pdf,
|
||||||
|
|
||||||
|
@RequestParam(value = "certType", required = false)
|
||||||
|
@Parameter(description = "The type of the digital certificate", schema = @Schema(allowableValues = {"PKCS12", "PEM"}))
|
||||||
|
String certType,
|
||||||
|
|
||||||
|
@RequestParam(value = "key", required = false)
|
||||||
|
@Parameter(description = "The private key for the digital certificate (required for PEM type certificates)")
|
||||||
|
MultipartFile privateKeyFile,
|
||||||
|
|
||||||
|
@RequestParam(value = "cert", required = false)
|
||||||
|
@Parameter(description = "The digital certificate (required for PEM type certificates)")
|
||||||
|
MultipartFile certFile,
|
||||||
|
|
||||||
|
@RequestParam(value = "p12", required = false)
|
||||||
|
@Parameter(description = "The PKCS12 keystore file (required for PKCS12 type certificates)")
|
||||||
|
MultipartFile p12File,
|
||||||
|
|
||||||
|
@RequestParam(value = "password", required = false)
|
||||||
|
@Parameter(description = "The password for the keystore or the private key")
|
||||||
|
String password,
|
||||||
|
|
||||||
|
@RequestParam(value = "showSignature", required = false)
|
||||||
|
@Parameter(description = "Whether to visually show the signature in the PDF file")
|
||||||
|
Boolean showSignature,
|
||||||
|
|
||||||
|
@RequestParam(value = "reason", required = false)
|
||||||
|
@Parameter(description = "The reason for signing the PDF")
|
||||||
|
String reason,
|
||||||
|
|
||||||
|
@RequestParam(value = "location", required = false)
|
||||||
|
@Parameter(description = "The location where the PDF is signed")
|
||||||
|
String location,
|
||||||
|
|
||||||
|
@RequestParam(value = "name", required = false)
|
||||||
|
@Parameter(description = "The name of the signer")
|
||||||
|
String name,
|
||||||
|
|
||||||
|
@RequestParam(value = "pageNumber", required = false)
|
||||||
|
@Parameter(description = "The page number where the signature should be visible. This is required if showSignature is set to true")
|
||||||
|
Integer pageNumber) throws Exception {
|
||||||
|
|
||||||
|
BouncyCastleProvider provider = new BouncyCastleProvider();
|
||||||
|
Security.addProvider(provider);
|
||||||
|
|
||||||
|
PrivateKey privateKey = null;
|
||||||
|
X509Certificate cert = null;
|
||||||
|
|
||||||
|
if (certType != null) {
|
||||||
|
switch (certType) {
|
||||||
|
case "PKCS12":
|
||||||
|
if (p12File != null) {
|
||||||
|
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||||
|
ks.load(new ByteArrayInputStream(p12File.getBytes()), password.toCharArray());
|
||||||
|
String alias = ks.aliases().nextElement();
|
||||||
|
privateKey = (PrivateKey) ks.getKey(alias, password.toCharArray());
|
||||||
|
cert = (X509Certificate) ks.getCertificate(alias);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "PEM":
|
||||||
|
if (privateKeyFile != null && certFile != null) {
|
||||||
|
// Load private key
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA", provider);
|
||||||
|
if (isPEM(privateKeyFile.getBytes())) {
|
||||||
|
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(parsePEM(privateKeyFile.getBytes())));
|
||||||
|
} else {
|
||||||
|
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyFile.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load certificate
|
||||||
|
CertificateFactory certFactory = CertificateFactory.getInstance("X.509", provider);
|
||||||
|
if (isPEM(certFile.getBytes())) {
|
||||||
|
cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(parsePEM(certFile.getBytes())));
|
||||||
|
} else {
|
||||||
|
cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(certFile.getBytes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PDSignature signature = new PDSignature();
|
||||||
|
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
|
||||||
|
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
|
||||||
|
signature.setName(name);
|
||||||
|
signature.setLocation(location);
|
||||||
|
signature.setReason(reason);
|
||||||
|
|
||||||
|
// Load the PDF
|
||||||
|
try (PDDocument document = PDDocument.load(pdf.getBytes())) {
|
||||||
|
SignatureOptions signatureOptions = new SignatureOptions();
|
||||||
|
|
||||||
|
// If you want to show the signature
|
||||||
|
if (showSignature != null && showSignature) {
|
||||||
|
// Calculate signature field position based on your requirements
|
||||||
|
|
||||||
|
PDPage page = document.getPage(pageNumber - 1); // zero-based
|
||||||
|
|
||||||
|
PDVisibleSignDesigner signDesigner = new PDVisibleSignDesigner(new ByteArrayInputStream(pdf.getBytes()));
|
||||||
|
//TODO signDesigner
|
||||||
|
|
||||||
|
PDVisibleSigProperties sigProperties = new PDVisibleSigProperties();
|
||||||
|
|
||||||
|
//TODO sigProperties extra
|
||||||
|
signatureOptions.setVisualSignature(sigProperties);
|
||||||
|
signatureOptions.setPage(pageNumber - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addSignature(signature, signatureOptions);
|
||||||
|
|
||||||
|
// External signing
|
||||||
|
ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(new ByteArrayOutputStream());
|
||||||
|
|
||||||
|
byte[] content = IOUtils.toByteArray(externalSigning.getContent());
|
||||||
|
|
||||||
|
// Using BouncyCastle to sign
|
||||||
|
CMSTypedData cmsData = new CMSProcessableByteArray(content);
|
||||||
|
|
||||||
|
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
|
||||||
|
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider(provider).build(privateKey);
|
||||||
|
|
||||||
|
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
|
||||||
|
new JcaDigestCalculatorProviderBuilder().setProvider(provider).build()).build(signer, cert));
|
||||||
|
|
||||||
|
gen.addCertificates(new JcaCertStore(Collections.singletonList(cert)));
|
||||||
|
CMSSignedData signedData = gen.generate(cmsData, false);
|
||||||
|
|
||||||
|
byte[] cmsSignature = signedData.getEncoded();
|
||||||
|
|
||||||
|
externalSigning.setSignature(cmsSignature);
|
||||||
|
|
||||||
|
|
||||||
|
// After setting the signature, return the resultant PDF
|
||||||
|
try (ByteArrayOutputStream signedPdfOutput = new ByteArrayOutputStream()) {
|
||||||
|
document.save(signedPdfOutput);
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||||
|
headers.setContentDisposition(ContentDisposition.builder("attachment").filename("signed.pdf").build());
|
||||||
|
|
||||||
|
return new ResponseEntity<>(signedPdfOutput.toByteArray(), headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] parsePEM(byte[] content) throws IOException {
|
||||||
|
PemReader pemReader = new PemReader(new InputStreamReader(new ByteArrayInputStream(content)));
|
||||||
|
return pemReader.readPemObject().getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPEM(byte[] content) {
|
||||||
|
String contentStr = new String(content);
|
||||||
|
return contentStr.contains("-----BEGIN") && contentStr.contains("-----END");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -10,14 +10,52 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import org.apache.xmpbox.xml.DomXmpParser;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.pdfbox.cos.COSDictionary;
|
||||||
|
import org.apache.pdfbox.cos.COSDocument;
|
||||||
|
import org.apache.pdfbox.cos.COSInputStream;
|
||||||
|
import org.apache.pdfbox.cos.COSName;
|
||||||
|
import org.apache.pdfbox.cos.COSObject;
|
||||||
|
import org.apache.pdfbox.cos.COSStream;
|
||||||
import org.apache.pdfbox.cos.COSString;
|
import org.apache.pdfbox.cos.COSString;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDJavascriptNameTreeNode;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDResources;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.COSObjectable;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDMetadata;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
|
||||||
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
|
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
|
||||||
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureNode;
|
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureNode;
|
||||||
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
|
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
|
||||||
import org.apache.pdfbox.pdmodel.encryption.PDEncryption;
|
import org.apache.pdfbox.pdmodel.encryption.PDEncryption;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentProperties;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationFileAttachment;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
|
||||||
|
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
|
||||||
import org.apache.pdfbox.text.PDFTextStripper;
|
import org.apache.pdfbox.text.PDFTextStripper;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
@ -25,33 +63,13 @@ 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 org.apache.xmpbox.XMPMetadata;
|
||||||
|
import org.apache.xmpbox.xml.XmpParsingException;
|
||||||
|
import org.apache.xmpbox.xml.XmpSerializer;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.itextpdf.forms.PdfAcroForm;
|
|
||||||
import com.itextpdf.forms.fields.PdfFormField;
|
|
||||||
import com.itextpdf.kernel.geom.Rectangle;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfArray;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfCatalog;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfDictionary;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfDocument;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfName;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfObject;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfOutline;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfReader;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfResources;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfStream;
|
|
||||||
import com.itextpdf.kernel.pdf.PdfString;
|
|
||||||
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
|
|
||||||
import com.itextpdf.kernel.pdf.annot.PdfFileAttachmentAnnotation;
|
|
||||||
import com.itextpdf.kernel.pdf.annot.PdfLinkAnnotation;
|
|
||||||
import com.itextpdf.kernel.pdf.layer.PdfLayer;
|
|
||||||
import com.itextpdf.kernel.pdf.layer.PdfOCProperties;
|
|
||||||
import com.itextpdf.kernel.xmp.XMPException;
|
|
||||||
import com.itextpdf.kernel.xmp.XMPMeta;
|
|
||||||
import com.itextpdf.kernel.xmp.XMPMetaFactory;
|
|
||||||
import com.itextpdf.kernel.xmp.options.SerializeOptions;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -72,7 +90,6 @@ public class GetInfoOnPDF {
|
||||||
|
|
||||||
try (
|
try (
|
||||||
PDDocument pdfBoxDoc = PDDocument.load(inputFile.getInputStream());
|
PDDocument pdfBoxDoc = PDDocument.load(inputFile.getInputStream());
|
||||||
PdfDocument itextDoc = new PdfDocument(new PdfReader(inputFile.getInputStream()))
|
|
||||||
) {
|
) {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
ObjectNode jsonOutput = objectMapper.createObjectNode();
|
ObjectNode jsonOutput = objectMapper.createObjectNode();
|
||||||
|
@ -120,21 +137,17 @@ public class GetInfoOnPDF {
|
||||||
boolean hasCompression = false;
|
boolean hasCompression = false;
|
||||||
String compressionType = "None";
|
String compressionType = "None";
|
||||||
|
|
||||||
// Check for object streams
|
COSDocument cosDoc = pdfBoxDoc.getDocument();
|
||||||
for (int i = 1; i <= itextDoc.getNumberOfPdfObjects(); i++) {
|
for (COSObject cosObject : cosDoc.getObjects()) {
|
||||||
PdfObject obj = itextDoc.getPdfObject(i);
|
if (cosObject.getObject() instanceof COSStream) {
|
||||||
if (obj != null && obj.isStream() && ((PdfStream) obj).get(PdfName.Type) == PdfName.ObjStm) {
|
COSStream cosStream = (COSStream) cosObject.getObject();
|
||||||
hasCompression = true;
|
if (COSName.OBJ_STM.equals(cosStream.getItem(COSName.TYPE))) {
|
||||||
compressionType = "Object Streams";
|
hasCompression = true;
|
||||||
break;
|
compressionType = "Object Streams";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not compressed using object streams, check for compressed Xref tables
|
|
||||||
if (!hasCompression && itextDoc.getReader().hasRebuiltXref()) {
|
|
||||||
hasCompression = true;
|
|
||||||
compressionType = "Compressed Xref or Rebuilt Xref";
|
|
||||||
}
|
|
||||||
basicInfo.put("Compression", hasCompression);
|
basicInfo.put("Compression", hasCompression);
|
||||||
if(hasCompression)
|
if(hasCompression)
|
||||||
basicInfo.put("CompressionType", compressionType);
|
basicInfo.put("CompressionType", compressionType);
|
||||||
|
@ -144,9 +157,8 @@ public class GetInfoOnPDF {
|
||||||
basicInfo.put("Number of pages", pdfBoxDoc.getNumberOfPages());
|
basicInfo.put("Number of pages", pdfBoxDoc.getNumberOfPages());
|
||||||
|
|
||||||
|
|
||||||
// Page Mode using iText7
|
PDDocumentCatalog catalog = pdfBoxDoc.getDocumentCatalog();
|
||||||
PdfCatalog catalog = itextDoc.getCatalog();
|
String pageMode = catalog.getPageMode().name();
|
||||||
PdfName pageMode = catalog.getPdfObject().getAsName(PdfName.PageMode);
|
|
||||||
|
|
||||||
// Document Information using PDFBox
|
// Document Information using PDFBox
|
||||||
docInfoNode.put("PDF version", pdfBoxDoc.getVersion());
|
docInfoNode.put("PDF version", pdfBoxDoc.getVersion());
|
||||||
|
@ -157,49 +169,54 @@ public class GetInfoOnPDF {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(itextDoc, false);
|
PDAcroForm acroForm = pdfBoxDoc.getDocumentCatalog().getAcroForm();
|
||||||
|
|
||||||
ObjectNode formFieldsNode = objectMapper.createObjectNode();
|
ObjectNode formFieldsNode = objectMapper.createObjectNode();
|
||||||
if (acroForm != null) {
|
if (acroForm != null) {
|
||||||
for (Map.Entry<String, PdfFormField> entry : acroForm.getFormFields().entrySet()) {
|
for (PDField field : acroForm.getFieldTree()) {
|
||||||
formFieldsNode.put(entry.getKey(), entry.getValue().getValueAsString());
|
formFieldsNode.put(field.getFullyQualifiedName(), field.getValueAsString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsonOutput.set("FormFields", formFieldsNode);
|
jsonOutput.set("FormFields", formFieldsNode);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//embeed files TODO size
|
//embeed files TODO size
|
||||||
|
PDEmbeddedFilesNameTreeNode efTree = catalog.getNames().getEmbeddedFiles();
|
||||||
|
|
||||||
ArrayNode embeddedFilesArray = objectMapper.createArrayNode();
|
ArrayNode embeddedFilesArray = objectMapper.createArrayNode();
|
||||||
if(itextDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Names) != null)
|
if (efTree != null) {
|
||||||
{
|
Map<String, PDComplexFileSpecification> efMap = efTree.getNames();
|
||||||
PdfDictionary embeddedFiles = itextDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Names)
|
if (efMap != null) {
|
||||||
.getAsDictionary(PdfName.EmbeddedFiles);
|
for (Map.Entry<String, PDComplexFileSpecification> entry : efMap.entrySet()) {
|
||||||
if (embeddedFiles != null) {
|
ObjectNode embeddedFileNode = objectMapper.createObjectNode();
|
||||||
|
embeddedFileNode.put("Name", entry.getKey());
|
||||||
PdfArray namesArray = embeddedFiles.getAsArray(PdfName.Names);
|
PDEmbeddedFile embeddedFile = entry.getValue().getEmbeddedFile();
|
||||||
if(namesArray != null) {
|
if (embeddedFile != null) {
|
||||||
for (int i = 0; i < namesArray.size(); i += 2) {
|
embeddedFileNode.put("FileSize", embeddedFile.getLength()); // size in bytes
|
||||||
ObjectNode embeddedFileNode = objectMapper.createObjectNode();
|
}
|
||||||
embeddedFileNode.put("Name", namesArray.getAsString(i).toString());
|
embeddedFilesArray.add(embeddedFileNode);
|
||||||
// Add other details if required
|
}
|
||||||
embeddedFilesArray.add(embeddedFileNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
other.set("EmbeddedFiles", embeddedFilesArray);
|
other.set("EmbeddedFiles", embeddedFilesArray);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//attachments TODO size
|
//attachments TODO size
|
||||||
ArrayNode attachmentsArray = objectMapper.createArrayNode();
|
ArrayNode attachmentsArray = objectMapper.createArrayNode();
|
||||||
for (int pageNum = 1; pageNum <= itextDoc.getNumberOfPages(); pageNum++) {
|
for (PDPage page : pdfBoxDoc.getPages()) {
|
||||||
for (PdfAnnotation annotation : itextDoc.getPage(pageNum).getAnnotations()) {
|
for (PDAnnotation annotation : page.getAnnotations()) {
|
||||||
if (annotation instanceof PdfFileAttachmentAnnotation) {
|
if (annotation instanceof PDAnnotationFileAttachment) {
|
||||||
|
PDAnnotationFileAttachment fileAttachmentAnnotation = (PDAnnotationFileAttachment) annotation;
|
||||||
|
|
||||||
ObjectNode attachmentNode = objectMapper.createObjectNode();
|
ObjectNode attachmentNode = objectMapper.createObjectNode();
|
||||||
attachmentNode.put("Name", ((PdfFileAttachmentAnnotation) annotation).getName().toString());
|
attachmentNode.put("Name", fileAttachmentAnnotation.getAttachmentName());
|
||||||
attachmentNode.put("Description", annotation.getContents().getValue());
|
attachmentNode.put("Description", fileAttachmentAnnotation.getContents());
|
||||||
|
|
||||||
attachmentsArray.add(attachmentNode);
|
attachmentsArray.add(attachmentNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,65 +224,54 @@ public class GetInfoOnPDF {
|
||||||
other.set("Attachments", attachmentsArray);
|
other.set("Attachments", attachmentsArray);
|
||||||
|
|
||||||
//Javascript
|
//Javascript
|
||||||
PdfDictionary namesDict = itextDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Names);
|
PDDocumentNameDictionary namesDict = catalog.getNames();
|
||||||
ArrayNode javascriptArray = objectMapper.createArrayNode();
|
ArrayNode javascriptArray = objectMapper.createArrayNode();
|
||||||
|
|
||||||
if (namesDict != null) {
|
if (namesDict != null) {
|
||||||
PdfDictionary javascriptDict = namesDict.getAsDictionary(PdfName.JavaScript);
|
PDJavascriptNameTreeNode javascriptDict = namesDict.getJavaScript();
|
||||||
if (javascriptDict != null) {
|
if (javascriptDict != null) {
|
||||||
|
try {
|
||||||
|
Map<String, PDActionJavaScript> jsEntries = javascriptDict.getNames();
|
||||||
|
|
||||||
PdfArray namesArray = javascriptDict.getAsArray(PdfName.Names);
|
for (Map.Entry<String, PDActionJavaScript> entry : jsEntries.entrySet()) {
|
||||||
for (int i = 0; i < namesArray.size(); i += 2) {
|
ObjectNode jsNode = objectMapper.createObjectNode();
|
||||||
ObjectNode jsNode = objectMapper.createObjectNode();
|
jsNode.put("JS Name", entry.getKey());
|
||||||
if(namesArray.getAsString(i) != null)
|
|
||||||
jsNode.put("JS Name", namesArray.getAsString(i).toString());
|
|
||||||
|
|
||||||
// Here we check for a PdfStream object and retrieve the JS code from it
|
PDActionJavaScript jsAction = entry.getValue();
|
||||||
PdfObject jsCode = namesArray.get(i+1);
|
if (jsAction != null) {
|
||||||
if (jsCode instanceof PdfStream) {
|
String jsCodeStr = jsAction.getAction();
|
||||||
byte[] jsCodeBytes = ((PdfStream)jsCode).getBytes();
|
if (jsCodeStr != null) {
|
||||||
String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
|
jsNode.put("JS Script Length", jsCodeStr.length());
|
||||||
jsNode.put("JS Script Length", jsCodeStr.length());
|
}
|
||||||
} else if (jsCode instanceof PdfDictionary) {
|
|
||||||
// If the JS code is in a dictionary, you'll need to know the key to use.
|
|
||||||
// Assuming the key is PdfName.JS:
|
|
||||||
PdfStream jsCodeStream = ((PdfDictionary)jsCode).getAsStream(PdfName.JS);
|
|
||||||
if (jsCodeStream != null) {
|
|
||||||
byte[] jsCodeBytes = jsCodeStream.getBytes();
|
|
||||||
String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
|
|
||||||
jsNode.put("JS Script Character Length", jsCodeStr.length());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
javascriptArray.add(jsNode);
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
javascriptArray.add(jsNode);
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other.set("JavaScript", javascriptArray);
|
other.set("JavaScript", javascriptArray);
|
||||||
|
|
||||||
|
|
||||||
//TODO size
|
//TODO size
|
||||||
PdfOCProperties ocProperties = itextDoc.getCatalog().getOCProperties(false);
|
PDOptionalContentProperties ocProperties = pdfBoxDoc.getDocumentCatalog().getOCProperties();
|
||||||
ArrayNode layersArray = objectMapper.createArrayNode();
|
ArrayNode layersArray = objectMapper.createArrayNode();
|
||||||
|
|
||||||
if (ocProperties != null) {
|
if (ocProperties != null) {
|
||||||
|
for (PDOptionalContentGroup ocg : ocProperties.getOptionalContentGroups()) {
|
||||||
for (PdfLayer layer : ocProperties.getLayers()) {
|
|
||||||
ObjectNode layerNode = objectMapper.createObjectNode();
|
ObjectNode layerNode = objectMapper.createObjectNode();
|
||||||
layerNode.put("Name", layer.getPdfObject().getAsString(PdfName.Name).toString());
|
layerNode.put("Name", ocg.getName());
|
||||||
layersArray.add(layerNode);
|
layersArray.add(layerNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
other.set("Layers", layersArray);
|
other.set("Layers", layersArray);
|
||||||
|
|
||||||
//TODO Security
|
//TODO Security
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Digital Signatures using iText7 TODO
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,13 +288,13 @@ public class GetInfoOnPDF {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
boolean isPdfACompliant = checkOutputIntent(itextDoc, "PDF/A");
|
boolean isPdfACompliant = checkForStandard(pdfBoxDoc, "PDF/A");
|
||||||
boolean isPdfXCompliant = checkOutputIntent(itextDoc, "PDF/X");
|
boolean isPdfXCompliant = checkForStandard(pdfBoxDoc, "PDF/X");
|
||||||
boolean isPdfECompliant = checkForStandard(itextDoc, "PDF/E");
|
boolean isPdfECompliant = checkForStandard(pdfBoxDoc, "PDF/E");
|
||||||
boolean isPdfVTCompliant = checkForStandard(itextDoc, "PDF/VT");
|
boolean isPdfVTCompliant = checkForStandard(pdfBoxDoc, "PDF/VT");
|
||||||
boolean isPdfUACompliant = checkForStandard(itextDoc, "PDF/UA");
|
boolean isPdfUACompliant = checkForStandard(pdfBoxDoc, "PDF/UA");
|
||||||
boolean isPdfBCompliant = checkForStandard(itextDoc, "PDF/B"); // If you want to check for PDF/Broadcast, though this isn't an official ISO standard.
|
boolean isPdfBCompliant = checkForStandard(pdfBoxDoc, "PDF/B"); // If you want to check for PDF/Broadcast, though this isn't an official ISO standard.
|
||||||
boolean isPdfSECCompliant = checkForStandard(itextDoc, "PDF/SEC"); // This might not be effective since PDF/SEC was under development in 2021.
|
boolean isPdfSECCompliant = checkForStandard(pdfBoxDoc, "PDF/SEC"); // This might not be effective since PDF/SEC was under development in 2021.
|
||||||
|
|
||||||
compliancy.put("IsPDF/ACompliant", isPdfACompliant);
|
compliancy.put("IsPDF/ACompliant", isPdfACompliant);
|
||||||
compliancy.put("IsPDF/XCompliant", isPdfXCompliant);
|
compliancy.put("IsPDF/XCompliant", isPdfXCompliant);
|
||||||
|
@ -302,27 +308,39 @@ public class GetInfoOnPDF {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PDOutlineNode root = pdfBoxDoc.getDocumentCatalog().getDocumentOutline();
|
||||||
ArrayNode bookmarksArray = objectMapper.createArrayNode();
|
ArrayNode bookmarksArray = objectMapper.createArrayNode();
|
||||||
PdfOutline root = itextDoc.getOutlines(false);
|
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
for (PdfOutline child : root.getAllChildren()) {
|
for (PDOutlineItem child : root.children()) {
|
||||||
addOutlinesToArray(child, bookmarksArray);
|
addOutlinesToArray(child, bookmarksArray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
other.set("Bookmarks/Outline/TOC", bookmarksArray);
|
other.set("Bookmarks/Outline/TOC", bookmarksArray);
|
||||||
|
|
||||||
byte[] xmpBytes = itextDoc.getXmpMetadata();
|
|
||||||
String xmpString = null;
|
|
||||||
if (xmpBytes != null) {
|
PDMetadata pdMetadata = pdfBoxDoc.getDocumentCatalog().getMetadata();
|
||||||
try {
|
|
||||||
XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(xmpBytes);
|
String xmpString = null;
|
||||||
xmpString = new String(XMPMetaFactory.serializeToBuffer(xmpMeta, new SerializeOptions()));
|
|
||||||
} catch (XMPException e) {
|
if (pdMetadata != null) {
|
||||||
e.printStackTrace();
|
try {
|
||||||
}
|
COSInputStream is = pdMetadata.createInputStream();
|
||||||
}
|
DomXmpParser domXmpParser = new DomXmpParser();
|
||||||
other.put("XMPMetadata", xmpString);
|
XMPMetadata xmpMeta = domXmpParser.parse(is);
|
||||||
|
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
new XmpSerializer().serialize(xmpMeta, os, true);
|
||||||
|
xmpString = new String(os.toByteArray(), StandardCharsets.UTF_8);
|
||||||
|
} catch (XmpParsingException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
other.put("XMPMetadata", xmpString);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (pdfBoxDoc.isEncrypted()) {
|
if (pdfBoxDoc.isEncrypted()) {
|
||||||
|
@ -356,23 +374,40 @@ public class GetInfoOnPDF {
|
||||||
|
|
||||||
|
|
||||||
ObjectNode pageInfoParent = objectMapper.createObjectNode();
|
ObjectNode pageInfoParent = objectMapper.createObjectNode();
|
||||||
for (int pageNum = 1; pageNum <= itextDoc.getNumberOfPages(); pageNum++) {
|
for (int pageNum = 1; pageNum <= pdfBoxDoc.getNumberOfPages(); pageNum++) {
|
||||||
ObjectNode pageInfo = objectMapper.createObjectNode();
|
ObjectNode pageInfo = objectMapper.createObjectNode();
|
||||||
|
|
||||||
|
// Retrieve the page
|
||||||
|
PDPage page = pdfBoxDoc.getPage(pageNum);
|
||||||
|
|
||||||
// Page-level Information
|
// Page-level Information
|
||||||
Rectangle pageSize = itextDoc.getPage(pageNum).getPageSize();
|
PDRectangle mediaBox = page.getMediaBox();
|
||||||
pageInfo.put("Width", pageSize.getWidth());
|
|
||||||
pageInfo.put("Height", pageSize.getHeight());
|
float width = mediaBox.getWidth();
|
||||||
pageInfo.put("Rotation", itextDoc.getPage(pageNum).getRotation());
|
float height = mediaBox.getHeight();
|
||||||
pageInfo.put("Page Orientation", getPageOrientation(pageSize.getWidth(),pageSize.getHeight()));
|
|
||||||
pageInfo.put("Standard Size", getPageSize(pageSize.getWidth(),pageSize.getHeight()));
|
pageInfo.put("Width", width);
|
||||||
|
pageInfo.put("Height", height);
|
||||||
|
pageInfo.put("Rotation", page.getRotation());
|
||||||
|
|
||||||
|
pageInfo.put("Page Orientation", getPageOrientation(width, height));
|
||||||
|
pageInfo.put("Standard Size", getPageSize(width, height));
|
||||||
|
|
||||||
// Boxes
|
// Boxes
|
||||||
pageInfo.put("MediaBox", itextDoc.getPage(pageNum).getMediaBox().toString());
|
pageInfo.put("MediaBox", mediaBox.toString());
|
||||||
pageInfo.put("CropBox", itextDoc.getPage(pageNum).getCropBox().toString());
|
|
||||||
pageInfo.put("BleedBox", itextDoc.getPage(pageNum).getBleedBox().toString());
|
// Assuming the following boxes are defined for your document; if not, you may get null values.
|
||||||
pageInfo.put("TrimBox", itextDoc.getPage(pageNum).getTrimBox().toString());
|
PDRectangle cropBox = page.getCropBox();
|
||||||
pageInfo.put("ArtBox", itextDoc.getPage(pageNum).getArtBox().toString());
|
pageInfo.put("CropBox", cropBox == null ? "Undefined" : cropBox.toString());
|
||||||
|
|
||||||
|
PDRectangle bleedBox = page.getBleedBox();
|
||||||
|
pageInfo.put("BleedBox", bleedBox == null ? "Undefined" : bleedBox.toString());
|
||||||
|
|
||||||
|
PDRectangle trimBox = page.getTrimBox();
|
||||||
|
pageInfo.put("TrimBox", trimBox == null ? "Undefined" : trimBox.toString());
|
||||||
|
|
||||||
|
PDRectangle artBox = page.getArtBox();
|
||||||
|
pageInfo.put("ArtBox", artBox == null ? "Undefined" : artBox.toString());
|
||||||
|
|
||||||
// Content Extraction
|
// Content Extraction
|
||||||
PDFTextStripper textStripper = new PDFTextStripper();
|
PDFTextStripper textStripper = new PDFTextStripper();
|
||||||
|
@ -382,17 +417,18 @@ public class GetInfoOnPDF {
|
||||||
|
|
||||||
pageInfo.put("Text Characters Count", pageText.length()); //
|
pageInfo.put("Text Characters Count", pageText.length()); //
|
||||||
|
|
||||||
// Annotations
|
// Annotations
|
||||||
List<PdfAnnotation> annotations = itextDoc.getPage(pageNum).getAnnotations();
|
|
||||||
|
List<PDAnnotation> annotations = page.getAnnotations();
|
||||||
|
|
||||||
int subtypeCount = 0;
|
int subtypeCount = 0;
|
||||||
int contentsCount = 0;
|
int contentsCount = 0;
|
||||||
|
|
||||||
for (PdfAnnotation annotation : annotations) {
|
for (PDAnnotation annotation : annotations) {
|
||||||
if(annotation.getSubtype() != null) {
|
if (annotation.getSubtype() != null) {
|
||||||
subtypeCount++; // Increase subtype count
|
subtypeCount++; // Increase subtype count
|
||||||
}
|
}
|
||||||
if(annotation.getContents() != null) {
|
if (annotation.getContents() != null) {
|
||||||
contentsCount++; // Increase contents count
|
contentsCount++; // Increase contents count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,25 +439,31 @@ public class GetInfoOnPDF {
|
||||||
annotationsObject.put("ContentsCount", contentsCount);
|
annotationsObject.put("ContentsCount", contentsCount);
|
||||||
pageInfo.set("Annotations", annotationsObject);
|
pageInfo.set("Annotations", annotationsObject);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Images (simplified)
|
// Images (simplified)
|
||||||
// This part is non-trivial as images can be embedded in multiple ways in a PDF.
|
// This part is non-trivial as images can be embedded in multiple ways in a PDF.
|
||||||
// Here is a basic structure to recognize image XObjects on a page.
|
// Here is a basic structure to recognize image XObjects on a page.
|
||||||
ArrayNode imagesArray = objectMapper.createArrayNode();
|
ArrayNode imagesArray = objectMapper.createArrayNode();
|
||||||
PdfResources resources = itextDoc.getPage(pageNum).getResources();
|
PDResources resources = page.getResources();
|
||||||
for (PdfName name : resources.getResourceNames()) {
|
|
||||||
PdfObject obj = resources.getResource(name);
|
|
||||||
if (obj instanceof PdfStream) {
|
for (COSName name : resources.getXObjectNames()) {
|
||||||
PdfStream stream = (PdfStream) obj;
|
PDXObject xObject = resources.getXObject(name);
|
||||||
if (PdfName.Image.equals(stream.getAsName(PdfName.Subtype))) {
|
if (xObject instanceof PDImageXObject) {
|
||||||
ObjectNode imageNode = objectMapper.createObjectNode();
|
PDImageXObject image = (PDImageXObject) xObject;
|
||||||
imageNode.put("Width", stream.getAsNumber(PdfName.Width).intValue());
|
|
||||||
imageNode.put("Height", stream.getAsNumber(PdfName.Height).intValue());
|
ObjectNode imageNode = objectMapper.createObjectNode();
|
||||||
PdfObject colorSpace = stream.get(PdfName.ColorSpace);
|
imageNode.put("Width", image.getWidth());
|
||||||
if (colorSpace != null) {
|
imageNode.put("Height", image.getHeight());
|
||||||
imageNode.put("ColorSpace", colorSpace.toString());
|
if(image.getMetadata() != null && image.getMetadata().getFile() != null && image.getMetadata().getFile().getFile() != null) {
|
||||||
}
|
imageNode.put("Name", image.getMetadata().getFile().getFile());
|
||||||
imagesArray.add(imageNode);
|
|
||||||
}
|
}
|
||||||
|
if (image.getColorSpace() != null) {
|
||||||
|
imageNode.put("ColorSpace", image.getColorSpace().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
imagesArray.add(imageNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pageInfo.set("Images", imagesArray);
|
pageInfo.set("Images", imagesArray);
|
||||||
|
@ -431,12 +473,13 @@ public class GetInfoOnPDF {
|
||||||
ArrayNode linksArray = objectMapper.createArrayNode();
|
ArrayNode linksArray = objectMapper.createArrayNode();
|
||||||
Set<String> uniqueURIs = new HashSet<>(); // To store unique URIs
|
Set<String> uniqueURIs = new HashSet<>(); // To store unique URIs
|
||||||
|
|
||||||
for (PdfAnnotation annotation : annotations) {
|
for (PDAnnotation annotation : annotations) {
|
||||||
if (annotation instanceof PdfLinkAnnotation) {
|
if (annotation instanceof PDAnnotationLink) {
|
||||||
PdfLinkAnnotation linkAnnotation = (PdfLinkAnnotation) annotation;
|
PDAnnotationLink linkAnnotation = (PDAnnotationLink) annotation;
|
||||||
if(linkAnnotation != null && linkAnnotation.getAction() != null) {
|
if (linkAnnotation.getAction() instanceof PDActionURI) {
|
||||||
String uri = linkAnnotation.getAction().toString();
|
PDActionURI uriAction = (PDActionURI) linkAnnotation.getAction();
|
||||||
uniqueURIs.add(uri); // Add to set to ensure uniqueness
|
String uri = uriAction.getURI();
|
||||||
|
uniqueURIs.add(uri); // Add to set to ensure uniqueness
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -449,96 +492,52 @@ public class GetInfoOnPDF {
|
||||||
}
|
}
|
||||||
pageInfo.set("Links", linksArray);
|
pageInfo.set("Links", linksArray);
|
||||||
|
|
||||||
// Fonts
|
|
||||||
|
// Fonts
|
||||||
ArrayNode fontsArray = objectMapper.createArrayNode();
|
ArrayNode fontsArray = objectMapper.createArrayNode();
|
||||||
PdfDictionary fontDicts = resources.getResource(PdfName.Font);
|
|
||||||
Set<String> uniqueSubtypes = new HashSet<>(); // To store unique subtypes
|
|
||||||
|
|
||||||
// Map to store unique fonts and their counts
|
|
||||||
Map<String, ObjectNode> uniqueFontsMap = new HashMap<>();
|
Map<String, ObjectNode> uniqueFontsMap = new HashMap<>();
|
||||||
|
|
||||||
if (fontDicts != null) {
|
for (COSName fontName : resources.getFontNames()) {
|
||||||
for (PdfName key : fontDicts.keySet()) {
|
PDFont font = resources.getFont(fontName);
|
||||||
ObjectNode fontNode = objectMapper.createObjectNode(); // Create a new font node for each font
|
ObjectNode fontNode = objectMapper.createObjectNode();
|
||||||
PdfDictionary font = fontDicts.getAsDictionary(key);
|
|
||||||
|
|
||||||
boolean isEmbedded = font.containsKey(PdfName.FontFile) ||
|
fontNode.put("IsEmbedded", font.isEmbedded());
|
||||||
font.containsKey(PdfName.FontFile2) ||
|
|
||||||
font.containsKey(PdfName.FontFile3);
|
|
||||||
fontNode.put("IsEmbedded", isEmbedded);
|
|
||||||
|
|
||||||
if (font.containsKey(PdfName.Encoding)) {
|
// PDFBox provides Font's BaseFont (i.e., the font name) directly
|
||||||
String encoding = font.getAsName(PdfName.Encoding).toString();
|
fontNode.put("Name", font.getName());
|
||||||
fontNode.put("Encoding", encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font.getAsString(PdfName.BaseFont) != null) {
|
fontNode.put("Subtype", font.getType());
|
||||||
fontNode.put("Name", font.getAsString(PdfName.BaseFont).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
String subtype = null;
|
PDFontDescriptor fontDescriptor = font.getFontDescriptor();
|
||||||
if (font.containsKey(PdfName.Subtype)) {
|
|
||||||
subtype = font.getAsName(PdfName.Subtype).toString();
|
|
||||||
uniqueSubtypes.add(subtype); // Add to set to ensure uniqueness
|
|
||||||
}
|
|
||||||
fontNode.put("Subtype", subtype);
|
|
||||||
|
|
||||||
PdfDictionary fontDescriptor = font.getAsDictionary(PdfName.FontDescriptor);
|
if (fontDescriptor != null) {
|
||||||
if (fontDescriptor != null) {
|
fontNode.put("ItalicAngle", fontDescriptor.getItalicAngle());
|
||||||
if (fontDescriptor.containsKey(PdfName.ItalicAngle)) {
|
int flags = fontDescriptor.getFlags();
|
||||||
fontNode.put("ItalicAngle", fontDescriptor.getAsNumber(PdfName.ItalicAngle).floatValue());
|
fontNode.put("IsItalic", (flags & 1) != 0);
|
||||||
}
|
fontNode.put("IsBold", (flags & 64) != 0);
|
||||||
|
fontNode.put("IsFixedPitch", (flags & 2) != 0);
|
||||||
|
fontNode.put("IsSerif", (flags & 4) != 0);
|
||||||
|
fontNode.put("IsSymbolic", (flags & 8) != 0);
|
||||||
|
fontNode.put("IsScript", (flags & 16) != 0);
|
||||||
|
fontNode.put("IsNonsymbolic", (flags & 32) != 0);
|
||||||
|
|
||||||
if (fontDescriptor.containsKey(PdfName.Flags)) {
|
fontNode.put("FontFamily", fontDescriptor.getFontFamily());
|
||||||
int flags = fontDescriptor.getAsNumber(PdfName.Flags).intValue();
|
// Font stretch and BBox are not directly available in PDFBox's API, so these are omitted for simplicity
|
||||||
fontNode.put("IsItalic", (flags & 64) != 0);
|
fontNode.put("FontWeight", fontDescriptor.getFontWeight());
|
||||||
fontNode.put("IsBold", (flags & 1 << 16) != 0);
|
}
|
||||||
fontNode.put("IsFixedPitch", (flags & 1) != 0);
|
|
||||||
fontNode.put("IsSerif", (flags & 2) != 0);
|
|
||||||
fontNode.put("IsSymbolic", (flags & 4) != 0);
|
|
||||||
fontNode.put("IsScript", (flags & 8) != 0);
|
|
||||||
fontNode.put("IsNonsymbolic", (flags & 16) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontDescriptor.containsKey(PdfName.FontFamily)) {
|
|
||||||
String fontFamily = fontDescriptor.getAsString(PdfName.FontFamily).toString();
|
|
||||||
fontNode.put("FontFamily", fontFamily);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontDescriptor.containsKey(PdfName.FontStretch)) {
|
// Create a unique key for this font node based on its attributes
|
||||||
String fontStretch = fontDescriptor.getAsName(PdfName.FontStretch).toString();
|
String uniqueKey = fontNode.toString();
|
||||||
fontNode.put("FontStretch", fontStretch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontDescriptor.containsKey(PdfName.FontBBox)) {
|
// Increment count if this font exists, or initialize it if new
|
||||||
PdfArray bbox = fontDescriptor.getAsArray(PdfName.FontBBox);
|
if (uniqueFontsMap.containsKey(uniqueKey)) {
|
||||||
fontNode.put("FontBoundingBox", bbox.toString());
|
ObjectNode existingFontNode = uniqueFontsMap.get(uniqueKey);
|
||||||
}
|
int count = existingFontNode.get("Count").asInt() + 1;
|
||||||
|
existingFontNode.put("Count", count);
|
||||||
if (fontDescriptor.containsKey(PdfName.FontWeight)) {
|
} else {
|
||||||
float fontWeight = fontDescriptor.getAsNumber(PdfName.FontWeight).floatValue();
|
fontNode.put("Count", 1);
|
||||||
fontNode.put("FontWeight", fontWeight);
|
uniqueFontsMap.put(uniqueKey, fontNode);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font.containsKey(PdfName.ToUnicode)) {
|
|
||||||
fontNode.put("HasToUnicodeMap", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontNode.size() > 0) {
|
|
||||||
// Create a unique key for this font node based on its attributes
|
|
||||||
String uniqueKey = fontNode.toString();
|
|
||||||
|
|
||||||
// Increment count if this font exists, or initialize it if new
|
|
||||||
if (uniqueFontsMap.containsKey(uniqueKey)) {
|
|
||||||
ObjectNode existingFontNode = uniqueFontsMap.get(uniqueKey);
|
|
||||||
int count = existingFontNode.get("Count").asInt() + 1;
|
|
||||||
existingFontNode.put("Count", count);
|
|
||||||
} else {
|
|
||||||
fontNode.put("Count", 1);
|
|
||||||
uniqueFontsMap.put(uniqueKey, fontNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,41 +551,49 @@ public class GetInfoOnPDF {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Access resources dictionary
|
|
||||||
PdfDictionary resourcesDict = itextDoc.getPage(pageNum).getResources().getPdfObject();
|
|
||||||
|
|
||||||
// Color Spaces & ICC Profiles
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Access resources dictionary
|
||||||
ArrayNode colorSpacesArray = objectMapper.createArrayNode();
|
ArrayNode colorSpacesArray = objectMapper.createArrayNode();
|
||||||
PdfDictionary colorSpaces = resourcesDict.getAsDictionary(PdfName.ColorSpace);
|
|
||||||
if (colorSpaces != null) {
|
Iterable<COSName> colorSpaceNames = resources.getColorSpaceNames();
|
||||||
for (PdfName name : colorSpaces.keySet()) {
|
for (COSName name : colorSpaceNames) {
|
||||||
PdfObject colorSpaceObject = colorSpaces.get(name);
|
PDColorSpace colorSpace = resources.getColorSpace(name);
|
||||||
if (colorSpaceObject instanceof PdfArray) {
|
if (colorSpace instanceof PDICCBased) {
|
||||||
PdfArray colorSpaceArray = (PdfArray) colorSpaceObject;
|
PDICCBased iccBased = (PDICCBased) colorSpace;
|
||||||
if (colorSpaceArray.size() > 1 && colorSpaceArray.get(0) instanceof PdfName && PdfName.ICCBased.equals(colorSpaceArray.get(0))) {
|
PDStream iccData = iccBased.getPDStream();
|
||||||
ObjectNode iccProfileNode = objectMapper.createObjectNode();
|
byte[] iccBytes = iccData.toByteArray();
|
||||||
PdfStream iccStream = (PdfStream) colorSpaceArray.get(1);
|
|
||||||
byte[] iccData = iccStream.getBytes();
|
// TODO: Further decode and analyze the ICC data if needed
|
||||||
// TODO: Further decode and analyze the ICC data if needed
|
ObjectNode iccProfileNode = objectMapper.createObjectNode();
|
||||||
iccProfileNode.put("ICC Profile Length", iccData.length);
|
iccProfileNode.put("ICC Profile Length", iccBytes.length);
|
||||||
colorSpacesArray.add(iccProfileNode);
|
colorSpacesArray.add(iccProfileNode);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pageInfo.set("Color Spaces & ICC Profiles", colorSpacesArray);
|
pageInfo.set("Color Spaces & ICC Profiles", colorSpacesArray);
|
||||||
|
|
||||||
|
|
||||||
// Other XObjects
|
// Other XObjects
|
||||||
Map<String, Integer> xObjectCountMap = new HashMap<>(); // To store the count for each type
|
Map<String, Integer> xObjectCountMap = new HashMap<>(); // To store the count for each type
|
||||||
PdfDictionary xObjects = resourcesDict.getAsDictionary(PdfName.XObject);
|
for (COSName name : resources.getXObjectNames()) {
|
||||||
if (xObjects != null) {
|
PDXObject xObject = resources.getXObject(name);
|
||||||
for (PdfName name : xObjects.keySet()) {
|
String xObjectType;
|
||||||
PdfStream xObjectStream = xObjects.getAsStream(name);
|
|
||||||
String xObjectType = xObjectStream.getAsName(PdfName.Subtype).toString();
|
if (xObject instanceof PDImageXObject) {
|
||||||
|
xObjectType = "Image";
|
||||||
// Increment the count for this type in the map
|
} else if (xObject instanceof PDFormXObject) {
|
||||||
xObjectCountMap.put(xObjectType, xObjectCountMap.getOrDefault(xObjectType, 0) + 1);
|
xObjectType = "Form";
|
||||||
|
} else {
|
||||||
|
xObjectType = "Other";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Increment the count for this type in the map
|
||||||
|
xObjectCountMap.put(xObjectType, xObjectCountMap.getOrDefault(xObjectType, 0) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the count map to pageInfo (or wherever you want to store it)
|
// Add the count map to pageInfo (or wherever you want to store it)
|
||||||
|
@ -598,14 +605,17 @@ public class GetInfoOnPDF {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ArrayNode multimediaArray = objectMapper.createArrayNode();
|
ArrayNode multimediaArray = objectMapper.createArrayNode();
|
||||||
for (PdfAnnotation annotation : annotations) {
|
|
||||||
if (PdfName.RichMedia.equals(annotation.getSubtype())) {
|
for (PDAnnotation annotation : annotations) {
|
||||||
|
if ("RichMedia".equals(annotation.getSubtype())) {
|
||||||
ObjectNode multimediaNode = objectMapper.createObjectNode();
|
ObjectNode multimediaNode = objectMapper.createObjectNode();
|
||||||
// Extract details from the dictionary as needed
|
// Extract details from the annotation as needed
|
||||||
multimediaArray.add(multimediaNode);
|
multimediaArray.add(multimediaNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pageInfo.set("Multimedia", multimediaArray);
|
pageInfo.set("Multimedia", multimediaArray);
|
||||||
|
|
||||||
|
|
||||||
|
@ -636,17 +646,21 @@ public class GetInfoOnPDF {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addOutlinesToArray(PdfOutline outline, ArrayNode arrayNode) {
|
private static void addOutlinesToArray(PDOutlineItem outline, ArrayNode arrayNode) {
|
||||||
if (outline == null) return;
|
if (outline == null) return;
|
||||||
|
|
||||||
ObjectNode outlineNode = objectMapper.createObjectNode();
|
ObjectNode outlineNode = objectMapper.createObjectNode();
|
||||||
outlineNode.put("Title", outline.getTitle());
|
outlineNode.put("Title", outline.getTitle());
|
||||||
// You can add other properties if needed
|
// You can add other properties if needed
|
||||||
arrayNode.add(outlineNode);
|
arrayNode.add(outlineNode);
|
||||||
|
|
||||||
for (PdfOutline child : outline.getAllChildren()) {
|
PDOutlineItem child = outline.getFirstChild();
|
||||||
|
while (child != null) {
|
||||||
addOutlinesToArray(child, arrayNode);
|
addOutlinesToArray(child, arrayNode);
|
||||||
|
child = child.getNextSibling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPageOrientation(double width, double height) {
|
public String getPageOrientation(double width, double height) {
|
||||||
if (width > height) {
|
if (width > height) {
|
||||||
return "Landscape";
|
return "Landscape";
|
||||||
|
@ -678,45 +692,33 @@ public class GetInfoOnPDF {
|
||||||
return Math.abs(pageAspectRatio - aspectRatio) <= 0.05;
|
return Math.abs(pageAspectRatio - aspectRatio) <= 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkForStandard(PdfDocument document, String standardKeyword) {
|
|
||||||
// Check Output Intents
|
|
||||||
boolean foundInOutputIntents = checkOutputIntent(document, standardKeyword);
|
|
||||||
if (foundInOutputIntents) return true;
|
|
||||||
|
|
||||||
// Check XMP Metadata (rudimentary)
|
|
||||||
try {
|
|
||||||
byte[] metadataBytes = document.getXmpMetadata();
|
|
||||||
if (metadataBytes != null) {
|
|
||||||
XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(metadataBytes);
|
|
||||||
String xmpString = xmpMeta.dumpObject();
|
|
||||||
if (xmpString.contains(standardKeyword)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (XMPException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean checkOutputIntent(PdfDocument document, String standard) {
|
public static boolean checkForStandard(PDDocument document, String standardKeyword) {
|
||||||
PdfArray outputIntents = document.getCatalog().getPdfObject().getAsArray(PdfName.OutputIntents);
|
// Check XMP Metadata
|
||||||
if (outputIntents != null && !outputIntents.isEmpty()) {
|
try {
|
||||||
for (int i = 0; i < outputIntents.size(); i++) {
|
PDMetadata pdMetadata = document.getDocumentCatalog().getMetadata();
|
||||||
PdfDictionary outputIntentDict = outputIntents.getAsDictionary(i);
|
if (pdMetadata != null) {
|
||||||
if (outputIntentDict != null) {
|
COSInputStream metaStream = pdMetadata.createInputStream();
|
||||||
PdfString s = outputIntentDict.getAsString(PdfName.S);
|
DomXmpParser domXmpParser = new DomXmpParser();
|
||||||
if (s != null && s.toString().contains(standard)) {
|
XMPMetadata xmpMeta = domXmpParser.parse(metaStream);
|
||||||
return true;
|
|
||||||
}
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
}
|
new XmpSerializer().serialize(xmpMeta, baos, true);
|
||||||
|
String xmpString = new String(baos.toByteArray(), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
if (xmpString.contains(standardKeyword)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
} catch (Exception e) { // Catching general exception for brevity, ideally you'd catch specific exceptions.
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ArrayNode exploreStructureTree(List<Object> nodes) {
|
public ArrayNode exploreStructureTree(List<Object> nodes) {
|
||||||
ArrayNode elementsArray = objectMapper.createArrayNode();
|
ArrayNode elementsArray = objectMapper.createArrayNode();
|
||||||
if (nodes != null) {
|
if (nodes != null) {
|
||||||
|
@ -771,7 +773,7 @@ public class GetInfoOnPDF {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPageModeDescription(PdfName pageMode) {
|
private String getPageModeDescription(String pageMode) {
|
||||||
return pageMode != null ? pageMode.toString().replaceFirst("/", "") : "Unknown";
|
return pageMode != null ? pageMode.toString().replaceFirst("/", "") : "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,6 @@ public class ApplicationProperties {
|
||||||
}
|
}
|
||||||
public static class Security {
|
public static class Security {
|
||||||
private Boolean enableLogin;
|
private Boolean enableLogin;
|
||||||
private InitialLogin initialLogin;
|
|
||||||
private Boolean csrfDisabled;
|
private Boolean csrfDisabled;
|
||||||
|
|
||||||
public Boolean getEnableLogin() {
|
public Boolean getEnableLogin() {
|
||||||
|
@ -115,14 +114,6 @@ public class ApplicationProperties {
|
||||||
this.enableLogin = enableLogin;
|
this.enableLogin = enableLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InitialLogin getInitialLogin() {
|
|
||||||
return initialLogin != null ? initialLogin : new InitialLogin();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInitialLogin(InitialLogin initialLogin) {
|
|
||||||
this.initialLogin = initialLogin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean getCsrfDisabled() {
|
public Boolean getCsrfDisabled() {
|
||||||
return csrfDisabled;
|
return csrfDisabled;
|
||||||
}
|
}
|
||||||
|
@ -134,40 +125,9 @@ public class ApplicationProperties {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Security [enableLogin=" + enableLogin + ", initialLogin=" + initialLogin + ", csrfDisabled="
|
return "Security [enableLogin=" + enableLogin + ", csrfDisabled="
|
||||||
+ csrfDisabled + "]";
|
+ csrfDisabled + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class InitialLogin {
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "InitialLogin [username=" + username + ", password=" + (password != null && !password.isEmpty() ? "MASKED" : "NULL") + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class System {
|
public static class System {
|
||||||
|
|
|
@ -65,7 +65,8 @@ public class GeneralUtils {
|
||||||
} else if (sizeStr.endsWith("B")) {
|
} else if (sizeStr.endsWith("B")) {
|
||||||
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
||||||
} else {
|
} else {
|
||||||
// Input string does not have a valid format, handle this case
|
// Assume MB if no unit is specified
|
||||||
|
return (long) (Double.parseDouble(sizeStr) * 1024 * 1024);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
// The numeric part of the input string cannot be parsed, handle this case
|
// The numeric part of the input string cannot be parsed, handle this case
|
||||||
|
|
|
@ -4,15 +4,11 @@
|
||||||
|
|
||||||
security:
|
security:
|
||||||
enableLogin: false # set to 'true' to enable login
|
enableLogin: false # set to 'true' to enable login
|
||||||
initialLogin:
|
|
||||||
username: 'username' # Specify the initial username for first boot (e.g. 'admin')
|
|
||||||
password: 'password' # Specify the initial password for first boot (e.g. 'password123')
|
|
||||||
csrfDisabled: true
|
csrfDisabled: true
|
||||||
|
|
||||||
system:
|
system:
|
||||||
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
||||||
googlevisibility: false # 'true' to allow Google visibility, 'false' to disallow
|
googlevisibility: false # 'true' to allow Google visibility, 'false' to disallow
|
||||||
rootURIPath: / # Set the application's root URI (e.g. /pdf-app)
|
|
||||||
customStaticFilePath: '/customFiles/static/' # Directory path for custom static files
|
customStaticFilePath: '/customFiles/static/' # Directory path for custom static files
|
||||||
|
|
||||||
#ui:
|
#ui:
|
||||||
|
|
Loading…
Reference in a new issue