watermark fix

This commit is contained in:
Anthony Stirling 2023-07-01 21:08:33 +01:00
parent f7832774d9
commit c140052822
11 changed files with 57 additions and 40 deletions

View file

@ -62,10 +62,6 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-core' implementation 'io.micrometer:micrometer-core'
// https://mvnrepository.com/artifact/com.cybozu.labs/langdetect
implementation group: 'com.cybozu.labs', name: 'langdetect', version: '1.1-20120112'
developmentOnly("org.springframework.boot:spring-boot-devtools") developmentOnly("org.springframework.boot:spring-boot-devtools")
} }

View file

@ -5,6 +5,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.util.Arrays;
import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
@ -23,14 +25,11 @@ 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.cybozu.labs.langdetect.Detector;
import com.cybozu.labs.langdetect.DetectorFactory;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
import io.swagger.v3.oas.annotations.media.Schema;
@RestController @RestController
@Tag(name = "Security", description = "Security APIs") @Tag(name = "Security", description = "Security APIs")
public class WatermarkController { public class WatermarkController {
@ -42,6 +41,12 @@ public class WatermarkController {
@RequestPart(required = true, value = "fileInput") @RequestPart(required = true, value = "fileInput")
@Parameter(description = "The input PDF file to add a watermark") @Parameter(description = "The input PDF file to add a watermark")
MultipartFile pdfFile, MultipartFile pdfFile,
@RequestParam(defaultValue = "roman", name = "alphabet")
@Parameter(description = "The selected alphabet",
schema = @Schema(type = "string",
allowableValues = {"roman","arabic","japanese","korean","chinese"},
defaultValue = "roman"))
String alphabet,
@RequestParam("watermarkText") @RequestParam("watermarkText")
@Parameter(description = "The watermark text to add to the PDF file") @Parameter(description = "The watermark text to add to the PDF file")
String watermarkText, String watermarkText,
@ -63,7 +68,7 @@ public class WatermarkController {
// Load the input PDF // Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream()); PDDocument document = PDDocument.load(pdfFile.getInputStream());
String producer = document.getDocumentInformation().getProducer();
// Create a page in the document // Create a page in the document
for (PDPage page : document.getPages()) { for (PDPage page : document.getPages()) {
@ -75,42 +80,39 @@ public class WatermarkController {
graphicsState.setNonStrokingAlphaConstant(opacity); graphicsState.setNonStrokingAlphaConstant(opacity);
contentStream.setGraphicsStateParameters(graphicsState); contentStream.setGraphicsStateParameters(graphicsState);
DetectorFactory.loadProfile("profiles");
Detector detector = DetectorFactory.create(); String resourceDir = "";
detector.append(watermarkText); PDFont font = PDType1Font.HELVETICA_BOLD;
String lang = detector.detect(); 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;
}
System.out.println("Detected lang" + lang);
// Set font of watermark
// Load NotoSans-Regular font from resources
String resourceDir = "";
PDFont font = PDType1Font.HELVETICA_BOLD;
switch (lang) {
case "ar":
resourceDir = "src/main/resources/static/fonts/NotoSansArabic-Regular.ttf";
break;
case "ja":
resourceDir = "src/main/resources/static/fonts/NotoSansJP-Regular.otf";
break;
case "ko":
resourceDir = "src/main/resources/static/fonts/NotoSansKR-Regular.otf";
break;
case "zh-cn":
resourceDir = "src/main/resources/static/fonts/NotoSansSC-Regular.otf";
break;
default:
font = PDType1Font.HELVETICA_BOLD;
}
if(!resourceDir.equals("")) { if(!resourceDir.equals("")) {
ClassPathResource classPathResource = new ClassPathResource("static/fonts/NotoSans-Regular.ttf"); ClassPathResource classPathResource = new ClassPathResource(resourceDir);
File tempFile = File.createTempFile("NotoSans-Regular", ".ttf"); String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
File tempFile = File.createTempFile("NotoSansFont", fileExtension);
try (InputStream is = classPathResource.getInputStream(); FileOutputStream os = new FileOutputStream(tempFile)) { try (InputStream is = classPathResource.getInputStream(); FileOutputStream os = new FileOutputStream(tempFile)) {
IOUtils.copy(is, os); IOUtils.copy(is, os);
} }
font = PDType0Font.load(document, tempFile); font = PDType0Font.load(document, tempFile);
tempFile.deleteOnExit();
} }
contentStream.beginText(); contentStream.beginText();
contentStream.setFont(font, fontSize); contentStream.setFont(font, fontSize);
@ -127,11 +129,19 @@ public class WatermarkController {
// Add the watermark text // Add the watermark text
for (int i = 0; i < watermarkRows; i++) { for (int i = 0; i < watermarkRows; i++) {
for (int j = 0; j < watermarkCols; j++) { for (int j = 0; j < watermarkCols; j++) {
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight));
if(producer.contains("Google Docs")) {
//This fixes weird unknown google docs y axis rotation/flip issue
//TODO: Long term fix one day
//contentStream.setTextMatrix(1, 0, 0, -1, j * watermarkWidth, pageHeight - i * watermarkHeight);
Matrix matrix = new Matrix(1, 0, 0, -1, j * watermarkWidth, pageHeight - i * watermarkHeight);
contentStream.setTextMatrix(matrix);
} else {
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight));
}
contentStream.showTextWithPositioning(new Object[] { watermarkText }); contentStream.showTextWithPositioning(new Object[] { watermarkText });
} }
} }
contentStream.endText(); contentStream.endText();
// Close the content stream // Close the content stream

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -18,6 +18,17 @@
<label th:text="#{watermark.selectText.1}"></label> <label th:text="#{watermark.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
</div> </div>
<div class="form-group">
<label for="fontSize" th:text="#{alphabet}"></label>
<select class="form-control" name="alphabet" id="alphabet-select">
<option value="romain">Roman</option>
<option value="arabic">العربية</option>
<option value="japanese">日本語</option>
<option value="korean">한국어</option>
<option value="chinese">简体中文</option>
</select>
</div>
<div class="form-group"> <div class="form-group">
<label for="watermarkText" th:text="#{watermark.selectText.2}"></label> <label for="watermarkText" th:text="#{watermark.selectText.2}"></label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required /> <input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required />