mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
cleanups
This commit is contained in:
parent
36e6fe03d8
commit
5aec97939e
@ -1,80 +0,0 @@
|
|||||||
package stirling.software.SPDF.controller.api.misc;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import io.github.pixee.security.Filenames;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import stirling.software.common.model.api.PDFFile;
|
|
||||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
|
||||||
import stirling.software.common.util.ProcessExecutor;
|
|
||||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
|
||||||
import stirling.software.common.util.TempFileManager;
|
|
||||||
import stirling.software.common.util.TempFileUtil;
|
|
||||||
import stirling.software.common.util.WebResponseUtils;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/v1/misc")
|
|
||||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
|
||||||
public class RepairController {
|
|
||||||
|
|
||||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
|
||||||
private final TempFileManager tempFileManager;
|
|
||||||
|
|
||||||
public RepairController(CustomPDFDocumentFactory pdfDocumentFactory, TempFileManager tempFileManager) {
|
|
||||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
|
||||||
this.tempFileManager = tempFileManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/repair")
|
|
||||||
@Operation(
|
|
||||||
summary = "Repair a PDF file",
|
|
||||||
description =
|
|
||||||
"This endpoint repairs a given PDF file by running qpdf command. The PDF is"
|
|
||||||
+ " first saved to a temporary location, repaired, read back, and then"
|
|
||||||
+ " returned as a response. Input:PDF Output:PDF Type:SISO")
|
|
||||||
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile file)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
MultipartFile inputFile = file.getFileInput();
|
|
||||||
|
|
||||||
// Use TempFileUtil.TempFile with try-with-resources for automatic cleanup
|
|
||||||
try (TempFileUtil.TempFile tempFile = new TempFileUtil.TempFile(tempFileManager, ".pdf")) {
|
|
||||||
// Save the uploaded file to the temporary location
|
|
||||||
inputFile.transferTo(tempFile.getFile());
|
|
||||||
|
|
||||||
List<String> command = new ArrayList<>();
|
|
||||||
command.add("qpdf");
|
|
||||||
command.add("--replace-input"); // Automatically fixes problems it can
|
|
||||||
command.add("--qdf"); // Linearizes and normalizes PDF structure
|
|
||||||
command.add("--object-streams=disable"); // Can help with some corruptions
|
|
||||||
command.add(tempFile.getFile().getAbsolutePath());
|
|
||||||
|
|
||||||
ProcessExecutorResult returnCode =
|
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
|
|
||||||
.runCommandWithOutputHandling(command);
|
|
||||||
|
|
||||||
// Read the optimized PDF file
|
|
||||||
byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempFile.getFile());
|
|
||||||
|
|
||||||
// Return the optimized PDF as a response
|
|
||||||
String outputFilename =
|
|
||||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
|
||||||
.replaceFirst("[.][^.]+$", "")
|
|
||||||
+ "_repaired.pdf";
|
|
||||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,344 +0,0 @@
|
|||||||
package stirling.software.SPDF.controller.api.misc;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
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.PDFont;
|
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
|
||||||
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
|
||||||
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
|
||||||
import org.apache.pdfbox.util.Matrix;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import io.github.pixee.security.Filenames;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.misc.AddStampRequest;
|
|
||||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
|
||||||
import stirling.software.common.util.TempFileManager;
|
|
||||||
import stirling.software.common.util.TempFileUtil;
|
|
||||||
import stirling.software.common.util.WebResponseUtils;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/v1/misc")
|
|
||||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class StampController {
|
|
||||||
|
|
||||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
|
||||||
private final stirling.software.common.util.TempFileManager tempFileManager;
|
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/add-stamp")
|
|
||||||
@Operation(
|
|
||||||
summary = "Add stamp to a PDF file",
|
|
||||||
description =
|
|
||||||
"This endpoint adds a stamp to a given PDF file. Users can specify the stamp"
|
|
||||||
+ " type (text or image), rotation, opacity, width spacer, and height"
|
|
||||||
+ " spacer. Input:PDF Output:PDF Type:SISO")
|
|
||||||
public ResponseEntity<byte[]> addStamp(@ModelAttribute AddStampRequest request)
|
|
||||||
throws IOException, Exception {
|
|
||||||
MultipartFile pdfFile = request.getFileInput();
|
|
||||||
String stampType = request.getStampType();
|
|
||||||
String stampText = request.getStampText();
|
|
||||||
MultipartFile stampImage = request.getStampImage();
|
|
||||||
String alphabet = request.getAlphabet();
|
|
||||||
float fontSize = request.getFontSize();
|
|
||||||
float rotation = request.getRotation();
|
|
||||||
float opacity = request.getOpacity();
|
|
||||||
int position = request.getPosition(); // Updated to use 1-9 positioning logic
|
|
||||||
float overrideX = request.getOverrideX(); // New field for X override
|
|
||||||
float overrideY = request.getOverrideY(); // New field for Y override
|
|
||||||
|
|
||||||
String customColor = request.getCustomColor();
|
|
||||||
float marginFactor;
|
|
||||||
|
|
||||||
switch (request.getCustomMargin().toLowerCase()) {
|
|
||||||
case "small":
|
|
||||||
marginFactor = 0.02f;
|
|
||||||
break;
|
|
||||||
case "medium":
|
|
||||||
marginFactor = 0.035f;
|
|
||||||
break;
|
|
||||||
case "large":
|
|
||||||
marginFactor = 0.05f;
|
|
||||||
break;
|
|
||||||
case "x-large":
|
|
||||||
marginFactor = 0.075f;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
marginFactor = 0.035f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the input PDF
|
|
||||||
PDDocument document = pdfDocumentFactory.load(pdfFile);
|
|
||||||
|
|
||||||
List<Integer> pageNumbers = request.getPageNumbersList(document, true);
|
|
||||||
|
|
||||||
for (int pageIndex : pageNumbers) {
|
|
||||||
int zeroBasedIndex = pageIndex - 1;
|
|
||||||
if (zeroBasedIndex >= 0 && zeroBasedIndex < document.getNumberOfPages()) {
|
|
||||||
PDPage page = document.getPage(zeroBasedIndex);
|
|
||||||
PDRectangle pageSize = page.getMediaBox();
|
|
||||||
float margin = marginFactor * (pageSize.getWidth() + pageSize.getHeight()) / 2;
|
|
||||||
|
|
||||||
PDPageContentStream contentStream =
|
|
||||||
new PDPageContentStream(
|
|
||||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
|
||||||
|
|
||||||
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
|
||||||
graphicsState.setNonStrokingAlphaConstant(opacity);
|
|
||||||
contentStream.setGraphicsStateParameters(graphicsState);
|
|
||||||
|
|
||||||
if ("text".equalsIgnoreCase(stampType)) {
|
|
||||||
addTextStamp(
|
|
||||||
contentStream,
|
|
||||||
stampText,
|
|
||||||
document,
|
|
||||||
page,
|
|
||||||
rotation,
|
|
||||||
position,
|
|
||||||
fontSize,
|
|
||||||
alphabet,
|
|
||||||
overrideX,
|
|
||||||
overrideY,
|
|
||||||
margin,
|
|
||||||
customColor);
|
|
||||||
} else if ("image".equalsIgnoreCase(stampType)) {
|
|
||||||
addImageStamp(
|
|
||||||
contentStream,
|
|
||||||
stampImage,
|
|
||||||
document,
|
|
||||||
page,
|
|
||||||
rotation,
|
|
||||||
position,
|
|
||||||
fontSize,
|
|
||||||
overrideX,
|
|
||||||
overrideY,
|
|
||||||
margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
contentStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
|
||||||
document,
|
|
||||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
|
||||||
.replaceFirst("[.][^.]+$", "")
|
|
||||||
+ "_stamped.pdf");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTextStamp(
|
|
||||||
PDPageContentStream contentStream,
|
|
||||||
String stampText,
|
|
||||||
PDDocument document,
|
|
||||||
PDPage page,
|
|
||||||
float rotation,
|
|
||||||
int position, // 1-9 positioning logic
|
|
||||||
float fontSize,
|
|
||||||
String alphabet,
|
|
||||||
float overrideX, // X override
|
|
||||||
float overrideY,
|
|
||||||
float margin,
|
|
||||||
String colorString) // Y override
|
|
||||||
throws IOException {
|
|
||||||
String resourceDir = "";
|
|
||||||
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
|
||||||
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 (!"".equals(resourceDir)) {
|
|
||||||
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
|
||||||
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
|
||||||
|
|
||||||
// Use TempFileUtil.TempFile with try-with-resources for automatic cleanup
|
|
||||||
try (TempFileUtil.TempFile tempFileWrapper = new TempFileUtil.TempFile(tempFileManager, fileExtension)) {
|
|
||||||
File tempFile = tempFileWrapper.getFile();
|
|
||||||
try (InputStream is = classPathResource.getInputStream();
|
|
||||||
FileOutputStream os = new FileOutputStream(tempFile)) {
|
|
||||||
IOUtils.copy(is, os);
|
|
||||||
font = PDType0Font.load(document, tempFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentStream.setFont(font, fontSize);
|
|
||||||
|
|
||||||
Color redactColor;
|
|
||||||
try {
|
|
||||||
if (!colorString.startsWith("#")) {
|
|
||||||
colorString = "#" + colorString;
|
|
||||||
}
|
|
||||||
redactColor = Color.decode(colorString);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
|
|
||||||
redactColor = Color.LIGHT_GRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentStream.setNonStrokingColor(redactColor);
|
|
||||||
|
|
||||||
PDRectangle pageSize = page.getMediaBox();
|
|
||||||
float x, y;
|
|
||||||
|
|
||||||
if (overrideX >= 0 && overrideY >= 0) {
|
|
||||||
// Use override values if provided
|
|
||||||
x = overrideX;
|
|
||||||
y = overrideY;
|
|
||||||
} else {
|
|
||||||
x = calculatePositionX(pageSize, position, fontSize, font, fontSize, stampText, margin);
|
|
||||||
y =
|
|
||||||
calculatePositionY(
|
|
||||||
pageSize, position, calculateTextCapHeight(font, fontSize), margin);
|
|
||||||
}
|
|
||||||
// Split the stampText into multiple lines
|
|
||||||
String[] lines = stampText.split("\\\\n");
|
|
||||||
|
|
||||||
// Calculate dynamic line height based on font ascent and descent
|
|
||||||
float ascent = font.getFontDescriptor().getAscent();
|
|
||||||
float descent = font.getFontDescriptor().getDescent();
|
|
||||||
float lineHeight = ((ascent - descent) / 1000) * fontSize;
|
|
||||||
|
|
||||||
contentStream.beginText();
|
|
||||||
for (int i = 0; i < lines.length; i++) {
|
|
||||||
String line = lines[i];
|
|
||||||
// Set the text matrix for each line with rotation
|
|
||||||
contentStream.setTextMatrix(
|
|
||||||
Matrix.getRotateInstance(Math.toRadians(rotation), x, y - (i * lineHeight)));
|
|
||||||
contentStream.showText(line);
|
|
||||||
}
|
|
||||||
contentStream.endText();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addImageStamp(
|
|
||||||
PDPageContentStream contentStream,
|
|
||||||
MultipartFile stampImage,
|
|
||||||
PDDocument document,
|
|
||||||
PDPage page,
|
|
||||||
float rotation,
|
|
||||||
int position, // 1-9 positioning logic
|
|
||||||
float fontSize,
|
|
||||||
float overrideX,
|
|
||||||
float overrideY,
|
|
||||||
float margin)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
// Load the stamp image
|
|
||||||
BufferedImage image = ImageIO.read(stampImage.getInputStream());
|
|
||||||
|
|
||||||
// Compute width based on original aspect ratio
|
|
||||||
float aspectRatio = (float) image.getWidth() / (float) image.getHeight();
|
|
||||||
|
|
||||||
// Desired physical height (in PDF points)
|
|
||||||
float desiredPhysicalHeight = fontSize;
|
|
||||||
|
|
||||||
// Desired physical width based on the aspect ratio
|
|
||||||
float desiredPhysicalWidth = desiredPhysicalHeight * aspectRatio;
|
|
||||||
|
|
||||||
// Convert the BufferedImage to PDImageXObject
|
|
||||||
PDImageXObject xobject = LosslessFactory.createFromImage(document, image);
|
|
||||||
|
|
||||||
PDRectangle pageSize = page.getMediaBox();
|
|
||||||
float x, y;
|
|
||||||
|
|
||||||
if (overrideX >= 0 && overrideY >= 0) {
|
|
||||||
// Use override values if provided
|
|
||||||
x = overrideX;
|
|
||||||
y = overrideY;
|
|
||||||
} else {
|
|
||||||
x = calculatePositionX(pageSize, position, desiredPhysicalWidth, null, 0, null, margin);
|
|
||||||
y = calculatePositionY(pageSize, position, fontSize, margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
contentStream.saveGraphicsState();
|
|
||||||
contentStream.transform(Matrix.getTranslateInstance(x, y));
|
|
||||||
contentStream.transform(Matrix.getRotateInstance(Math.toRadians(rotation), 0, 0));
|
|
||||||
contentStream.drawImage(xobject, 0, 0, desiredPhysicalWidth, desiredPhysicalHeight);
|
|
||||||
contentStream.restoreGraphicsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private float calculatePositionX(
|
|
||||||
PDRectangle pageSize,
|
|
||||||
int position,
|
|
||||||
float contentWidth,
|
|
||||||
PDFont font,
|
|
||||||
float fontSize,
|
|
||||||
String text,
|
|
||||||
float margin)
|
|
||||||
throws IOException {
|
|
||||||
float actualWidth =
|
|
||||||
(text != null) ? calculateTextWidth(text, font, fontSize) : contentWidth;
|
|
||||||
switch (position % 3) {
|
|
||||||
case 1: // Left
|
|
||||||
return pageSize.getLowerLeftX() + margin;
|
|
||||||
case 2: // Center
|
|
||||||
return (pageSize.getWidth() - actualWidth) / 2;
|
|
||||||
case 0: // Right
|
|
||||||
return pageSize.getUpperRightX() - actualWidth - margin;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float calculatePositionY(
|
|
||||||
PDRectangle pageSize, int position, float height, float margin) {
|
|
||||||
switch ((position - 1) / 3) {
|
|
||||||
case 0: // Top
|
|
||||||
return pageSize.getUpperRightY() - height - margin;
|
|
||||||
case 1: // Middle
|
|
||||||
return (pageSize.getHeight() - height) / 2;
|
|
||||||
case 2: // Bottom
|
|
||||||
return pageSize.getLowerLeftY() + margin;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float calculateTextWidth(String text, PDFont font, float fontSize) throws IOException {
|
|
||||||
return font.getStringWidth(text) / 1000 * fontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float calculateTextCapHeight(PDFont font, float fontSize) {
|
|
||||||
return font.getFontDescriptor().getCapHeight() / 1000 * fontSize;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
#############################################################################################################
|
|
||||||
# Welcome to settings file from #
|
|
||||||
# ____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____ #
|
|
||||||
# / ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___| #
|
|
||||||
# \___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_ #
|
|
||||||
# ___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _| #
|
|
||||||
# |____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_| #
|
|
||||||
# #
|
|
||||||
# Do not comment out any entry, it will be removed on next startup #
|
|
||||||
# If you want to override with environment parameter follow parameter naming SECURITY_INITIALLOGIN_USERNAME #
|
|
||||||
#############################################################################################################
|
|
||||||
|
|
||||||
security:
|
|
||||||
enableLogin: false # set to 'true' to enable login
|
|
||||||
csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production)
|
|
||||||
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
|
|
||||||
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
|
||||||
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
|
|
||||||
initialLogin:
|
|
||||||
username: '' # initial username for the first login
|
|
||||||
password: '' # initial password for the first login
|
|
||||||
oauth2:
|
|
||||||
enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
|
|
||||||
client:
|
|
||||||
keycloak:
|
|
||||||
issuer: '' # URL of the Keycloak realm's OpenID Connect Discovery endpoint
|
|
||||||
clientId: '' # client ID for Keycloak OAuth2
|
|
||||||
clientSecret: '' # client secret for Keycloak OAuth2
|
|
||||||
scopes: openid, profile, email # scopes for Keycloak OAuth2
|
|
||||||
useAsUsername: preferred_username # field to use as the username for Keycloak OAuth2. Available options are: [email | name | given_name | family_name | preferred_name]
|
|
||||||
google:
|
|
||||||
clientId: '' # client ID for Google OAuth2
|
|
||||||
clientSecret: '' # client secret for Google OAuth2
|
|
||||||
scopes: email, profile # scopes for Google OAuth2
|
|
||||||
useAsUsername: email # field to use as the username for Google OAuth2. Available options are: [email | name | given_name | family_name]
|
|
||||||
github:
|
|
||||||
clientId: '' # client ID for GitHub OAuth2
|
|
||||||
clientSecret: '' # client secret for GitHub OAuth2
|
|
||||||
scopes: read:user # scope for GitHub OAuth2
|
|
||||||
useAsUsername: login # field to use as the username for GitHub OAuth2. Available options are: [email | login | name]
|
|
||||||
issuer: '' # set to any Provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
|
|
||||||
clientId: '' # client ID from your Provider
|
|
||||||
clientSecret: '' # client secret from your Provider
|
|
||||||
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
|
||||||
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
|
||||||
useAsUsername: email # default is 'email'; custom fields can be used as the username
|
|
||||||
scopes: openid, profile, email # specify the scopes for which the application will request permissions
|
|
||||||
provider: google # set this to your OAuth Provider's name, e.g., 'google' or 'keycloak'
|
|
||||||
saml2:
|
|
||||||
enabled: false # Only enabled for paid enterprise clients (enterpriseEdition.enabled must be true)
|
|
||||||
provider: '' # The name of your Provider
|
|
||||||
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
|
||||||
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
|
||||||
registrationId: stirling # The name of your Service Provider (SP) app name. Should match the name in the path for your SSO & SLO URLs
|
|
||||||
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata # The uri for your Provider's metadata
|
|
||||||
idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml # The URL for initiating SSO. Provided by your Provider
|
|
||||||
idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml # The URL for initiating SLO. Provided by your Provider
|
|
||||||
idpIssuer: '' # The ID of your Provider
|
|
||||||
idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider
|
|
||||||
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
|
||||||
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
|
||||||
|
|
||||||
premium:
|
|
||||||
key: 00000000-0000-0000-0000-000000000000
|
|
||||||
enabled: true # Enable license key checks for pro/enterprise features
|
|
||||||
proFeatures:
|
|
||||||
database: true # Enable database features
|
|
||||||
SSOAutoLogin: false
|
|
||||||
CustomMetadata:
|
|
||||||
autoUpdateMetadata: false
|
|
||||||
author: username
|
|
||||||
creator: Stirling-PDF
|
|
||||||
producer: Stirling-PDF
|
|
||||||
googleDrive:
|
|
||||||
enabled: false
|
|
||||||
clientId: ''
|
|
||||||
apiKey: ''
|
|
||||||
appId: ''
|
|
||||||
|
|
||||||
mail:
|
|
||||||
enabled: false # set to 'true' to enable sending emails
|
|
||||||
host: smtp.example.com # SMTP server hostname
|
|
||||||
port: 587 # SMTP server port
|
|
||||||
username: '' # SMTP server username
|
|
||||||
password: '' # SMTP server password
|
|
||||||
from: '' # sender email address
|
|
||||||
|
|
||||||
legal:
|
|
||||||
termsAndConditions: https://www.stirlingpdf.com/terms # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
|
||||||
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
|
|
||||||
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
|
|
||||||
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
|
|
||||||
impressum: '' # URL to the impressum of your application (e.g. https://example.com/impressum). Empty string to disable or filename to load from local file in static folder
|
|
||||||
|
|
||||||
system:
|
|
||||||
defaultLocale: en-US # set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
|
||||||
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow
|
|
||||||
enableAlphaFunctionality: false # set to enable functionality which might need more testing before it fully goes live (this feature might make no changes)
|
|
||||||
showUpdate: false # see when a new update is available
|
|
||||||
showUpdateOnlyAdmin: false # only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
|
|
||||||
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
|
|
||||||
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
|
|
||||||
enableAnalytics: null # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
|
|
||||||
enableUrlToPDF: false # Set to 'true' to enable URL to PDF, INTERNAL ONLY, known security issues, should not be used externally
|
|
||||||
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
|
|
||||||
datasource:
|
|
||||||
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
|
|
||||||
customDatabaseUrl: '' # eg jdbc:postgresql://localhost:5432/postgres, set the url for your own custom database connection. If provided, the type, hostName, port and name are not necessary and will not be used
|
|
||||||
username: postgres # set the database username
|
|
||||||
password: postgres # set the database password
|
|
||||||
type: postgresql # the type of the database to set (e.g. 'h2', 'postgresql')
|
|
||||||
hostName: localhost # the host name to use for the database url. Set to 'localhost' when running the app locally. Set to match the name of the container name of your database container when running the app on a server (Docker configuration)
|
|
||||||
port: 5432 # set the port number of the database. Ensure this matches the port the database is listening to
|
|
||||||
name: postgres # set the name of your database. Should match the name of the database you create
|
|
||||||
customPaths:
|
|
||||||
pipeline:
|
|
||||||
watchedFoldersDir: '' # Defaults to /pipeline/watchedFolders
|
|
||||||
finishedFoldersDir: '' # Defaults to /pipeline/finishedFolders
|
|
||||||
operations:
|
|
||||||
weasyprint: '' # Defaults to /opt/venv/bin/weasyprint
|
|
||||||
unoconvert: '' # Defaults to /opt/venv/bin/unoconvert
|
|
||||||
fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB".
|
|
||||||
tempfiles:
|
|
||||||
prefix: stirling-pdf- # Prefix for all temporary files created by the application
|
|
||||||
directory: '' # If empty, defaults to java.io.tmpdir/stirling-pdf (AppData\Local\Temp\stirling-pdf on Windows)
|
|
||||||
libreoffice-dir: '' # If empty, defaults to java.io.tmpdir/stirling-pdf-libreoffice
|
|
||||||
max-age-hours: 4 # How long to keep temporary files
|
|
||||||
cleanup-interval-minutes: 30 # How often to run the cleanup process
|
|
||||||
startup-cleanup: true # Whether to clean temporary files on application startup
|
|
||||||
system-temp-dir: '' # If empty, defaults to java.io.tmpdir, e.g., /tmp on Linux or AppData\Local\Temp on Windows
|
|
||||||
|
|
||||||
ui:
|
|
||||||
appName: '' # application's visible name
|
|
||||||
homeDescription: '' # short description or tagline shown on the homepage
|
|
||||||
appNameNavbar: '' # name displayed on the navigation bar
|
|
||||||
languages: [] # If empty, all languages are enabled. To display only German and Polish ["de_DE", "pl_PL"]. British English is always enabled.
|
|
||||||
|
|
||||||
endpoints:
|
|
||||||
toRemove: [] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])
|
|
||||||
groupsToRemove: [] # list groups to disable (e.g. ['LibreOffice'])
|
|
||||||
|
|
||||||
metrics:
|
|
||||||
enabled: true # 'true' to enable Info APIs (`/api/*`) endpoints, 'false' to disable
|
|
||||||
|
|
||||||
# Automatically Generated Settings (Do Not Edit Directly)
|
|
||||||
AutomaticallyGenerated:
|
|
||||||
key: example
|
|
||||||
UUID: example
|
|
||||||
appVersion: 0.35.0
|
|
||||||
|
|
||||||
processExecutor:
|
|
||||||
sessionLimit: # Process executor instances limits
|
|
||||||
libreOfficeSessionLimit: 1
|
|
||||||
pdfToHtmlSessionLimit: 1
|
|
||||||
qpdfSessionLimit: 4
|
|
||||||
tesseractSessionLimit: 1
|
|
||||||
pythonOpenCvSessionLimit: 8
|
|
||||||
weasyPrintSessionLimit: 16
|
|
||||||
installAppSessionLimit: 1
|
|
||||||
calibreSessionLimit: 1
|
|
||||||
timeoutMinutes: # Process executor timeout in minutes
|
|
||||||
libreOfficetimeoutMinutes: 30
|
|
||||||
pdfToHtmltimeoutMinutes: 20
|
|
||||||
pythonOpenCvtimeoutMinutes: 30
|
|
||||||
weasyPrinttimeoutMinutes: 30
|
|
||||||
installApptimeoutMinutes: 60
|
|
||||||
calibretimeoutMinutes: 30
|
|
||||||
tesseractTimeoutMinutes: 30
|
|
@ -1,8 +1,8 @@
|
|||||||
package stirling.software.SPDF.controller.api.misc;
|
package stirling.software.SPDF.controller.api.misc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import stirling.software.common.util.TempFileManager;
|
||||||
import java.nio.file.Path;
|
import stirling.software.common.util.TempFileUtil;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ import stirling.software.common.util.WebResponseUtils;
|
|||||||
public class RepairController {
|
public class RepairController {
|
||||||
|
|
||||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
||||||
|
private final TempFileManager tempFileManager;
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/repair")
|
@PostMapping(consumes = "multipart/form-data", value = "/repair")
|
||||||
@Operation(
|
@Operation(
|
||||||
@ -43,25 +44,25 @@ public class RepairController {
|
|||||||
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile file)
|
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile file)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = file.getFileInput();
|
MultipartFile inputFile = file.getFileInput();
|
||||||
// Save the uploaded file to a temporary location
|
|
||||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
// Use TempFileUtil.TempFile with try-with-resources for automatic cleanup
|
||||||
byte[] pdfBytes = null;
|
try (TempFileUtil.TempFile tempFile = new TempFileUtil.TempFile(tempFileManager, ".pdf")) {
|
||||||
inputFile.transferTo(tempInputFile.toFile());
|
// Save the uploaded file to the temporary location
|
||||||
try {
|
inputFile.transferTo(tempFile.getFile());
|
||||||
|
|
||||||
List<String> command = new ArrayList<>();
|
List<String> command = new ArrayList<>();
|
||||||
command.add("qpdf");
|
command.add("qpdf");
|
||||||
command.add("--replace-input"); // Automatically fixes problems it can
|
command.add("--replace-input"); // Automatically fixes problems it can
|
||||||
command.add("--qdf"); // Linearizes and normalizes PDF structure
|
command.add("--qdf"); // Linearizes and normalizes PDF structure
|
||||||
command.add("--object-streams=disable"); // Can help with some corruptions
|
command.add("--object-streams=disable"); // Can help with some corruptions
|
||||||
command.add(tempInputFile.toString());
|
command.add(tempFile.getFile().getAbsolutePath());
|
||||||
|
|
||||||
ProcessExecutorResult returnCode =
|
ProcessExecutorResult returnCode =
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
|
||||||
.runCommandWithOutputHandling(command);
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
// Read the optimized PDF file
|
// Read the optimized PDF file
|
||||||
pdfBytes = pdfDocumentFactory.loadToBytes(tempInputFile.toFile());
|
byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempFile.getFile());
|
||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
@ -69,9 +70,6 @@ public class RepairController {
|
|||||||
.replaceFirst("[.][^.]+$", "")
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_repaired.pdf";
|
+ "_repaired.pdf";
|
||||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
} finally {
|
|
||||||
// Clean up the temporary files
|
|
||||||
Files.deleteIfExists(tempInputFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import stirling.software.common.util.TempFileManager;
|
||||||
|
import stirling.software.common.util.TempFileUtil;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
@ -49,6 +50,9 @@ import stirling.software.common.util.WebResponseUtils;
|
|||||||
public class StampController {
|
public class StampController {
|
||||||
|
|
||||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
||||||
|
private final TempFileManager tempFileManager;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/add-stamp")
|
@PostMapping(consumes = "multipart/form-data", value = "/add-stamp")
|
||||||
@Operation(
|
@Operation(
|
||||||
@ -188,14 +192,14 @@ public class StampController {
|
|||||||
if (!"".equals(resourceDir)) {
|
if (!"".equals(resourceDir)) {
|
||||||
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
||||||
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
||||||
File tempFile = Files.createTempFile("NotoSansFont", fileExtension).toFile();
|
|
||||||
try (InputStream is = classPathResource.getInputStream();
|
// Use TempFileUtil.TempFile with try-with-resources for automatic cleanup
|
||||||
FileOutputStream os = new FileOutputStream(tempFile)) {
|
try (TempFileUtil.TempFile tempFileWrapper = new TempFileUtil.TempFile(tempFileManager, fileExtension)) {
|
||||||
IOUtils.copy(is, os);
|
File tempFile = tempFileWrapper.getFile();
|
||||||
font = PDType0Font.load(document, tempFile);
|
try (InputStream is = classPathResource.getInputStream();
|
||||||
} finally {
|
FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||||
if (tempFile != null) {
|
IOUtils.copy(is, os);
|
||||||
Files.deleteIfExists(tempFile.toPath());
|
font = PDType0Font.load(document, tempFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user