page numbers and custom images
This commit is contained in:
parent
3c54429fe0
commit
f92482d89e
16 changed files with 317 additions and 8 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -16,6 +16,12 @@ local.properties
|
||||||
.project
|
.project
|
||||||
version.properties
|
version.properties
|
||||||
|
|
||||||
|
#### Stirling-PDF Files ###
|
||||||
|
customFiles/
|
||||||
|
config/
|
||||||
|
watchedFolders/
|
||||||
|
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
.lock
|
.lock
|
||||||
|
|
|
@ -10,6 +10,12 @@ RUN apt-get update && \
|
||||||
unoconv && \
|
unoconv && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
#Install fonts
|
||||||
|
RUN mkdir /usr/share/fonts/opentype/noto/
|
||||||
|
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||||
|
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
||||||
|
RUN fc-cache -f -v
|
||||||
|
|
||||||
# Copy the application JAR file
|
# Copy the application JAR file
|
||||||
COPY build/libs/*.jar app.jar
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,8 @@ docker run -d \
|
||||||
|
|
||||||
|
|
||||||
Can also add these for customisation but are not required
|
Can also add these for customisation but are not required
|
||||||
|
-v /location/of/extraConfigs:/configs \
|
||||||
|
-v /location/of/customFiles:/customFiles \
|
||||||
-e APP_HOME_NAME="Stirling PDF" \
|
-e APP_HOME_NAME="Stirling PDF" \
|
||||||
-e APP_HOME_DESCRIPTION="Your locally hosted one-stop-shop for all your PDF needs." \
|
-e APP_HOME_DESCRIPTION="Your locally hosted one-stop-shop for all your PDF needs." \
|
||||||
-e APP_NAVBAR_NAME="Stirling PDF" \
|
-e APP_NAVBAR_NAME="Stirling PDF" \
|
||||||
|
@ -104,6 +106,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata #Required for extra OCR languages
|
- /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata #Required for extra OCR languages
|
||||||
# - /location/of/extraConfigs:/configs
|
# - /location/of/extraConfigs:/configs
|
||||||
|
# - /location/of/customFiles:/customFiles/
|
||||||
# environment:
|
# environment:
|
||||||
# APP_LOCALE: en_GB
|
# APP_LOCALE: en_GB
|
||||||
# APP_HOME_NAME: Stirling PDF
|
# APP_HOME_NAME: Stirling PDF
|
||||||
|
@ -161,10 +164,11 @@ Using the same method you can also change
|
||||||
- Change root URI for Stirling-PDF ie change server.com/ to server.com/pdf-app by running APP_ROOT_PATH as pdf-app
|
- Change root URI for Stirling-PDF ie change server.com/ to server.com/pdf-app by running APP_ROOT_PATH as pdf-app
|
||||||
- Disable and remove endpoints and functionality from Stirling-PDF. Currently the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma seperated lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image to pdf and remove pages, GROUPS_TO_REMOVE=LibreOffice Would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Frooodle/Stirling-PDF/blob/main/groups.md)
|
- Disable and remove endpoints and functionality from Stirling-PDF. Currently the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma seperated lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image to pdf and remove pages, GROUPS_TO_REMOVE=LibreOffice Would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Frooodle/Stirling-PDF/blob/main/groups.md)
|
||||||
- Change the max file size allowed through the server with the environment variable MAX_FILE_SIZE. default 2000MB
|
- Change the max file size allowed through the server with the environment variable MAX_FILE_SIZE. default 2000MB
|
||||||
|
- Customise static files such as app logo by placing files in the /customFiles/static/ directory. Example to customise app logo is placing a /customFiles/static/favicon.svg to override current SVG. This can be used to change any images/icons/css/fonts/js etc in Stirling-PDF
|
||||||
|
|
||||||
## API
|
## API
|
||||||
For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation
|
For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation
|
||||||
[here](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation
|
[here](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation (Or by following the API button in your settings of Stirling-PDF)
|
||||||
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package stirling.software.SPDF;
|
package stirling.software.SPDF;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
@ -7,6 +12,7 @@ import org.springframework.core.env.Environment;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
//@EnableScheduling
|
//@EnableScheduling
|
||||||
|
@ -49,6 +55,10 @@ public class SPdfApplication {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GeneralUtils.createDir("customFiles/static/");
|
||||||
|
GeneralUtils.createDir("customFiles/templates/");
|
||||||
|
GeneralUtils.createDir("config");
|
||||||
System.out.println("Stirling-PDF Started.");
|
System.out.println("Stirling-PDF Started.");
|
||||||
|
|
||||||
String port = System.getProperty("local.server.port");
|
String port = System.getProperty("local.server.port");
|
||||||
|
|
|
@ -3,6 +3,7 @@ package stirling.software.SPDF.config;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -15,4 +16,12 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(endpointInterceptor);
|
registry.addInterceptor(endpointInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
// Handler for external static resources
|
||||||
|
registry.addResourceHandler("/**")
|
||||||
|
.addResourceLocations("file:customFiles/static/", "classpath:/static/")
|
||||||
|
.setCachePeriod(0); // Optional: disable caching
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,11 @@ public class ScalePagesController {
|
||||||
@Operation(summary = "Change the size of a PDF page/document", description = "This operation takes an input PDF file and the size to scale the pages to in the output PDF file. Input:PDF Output:PDF Type:SISO")
|
@Operation(summary = "Change the size of a PDF page/document", description = "This operation takes an input PDF file and the size to scale the pages to in the output PDF file. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> scalePages(
|
public ResponseEntity<byte[]> scalePages(
|
||||||
@Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file,
|
@Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file,
|
||||||
@Parameter(description = "The scale of pages in the output PDF. Acceptable values are A0-A10, B0-B9, LETTER, TABLOID, LEDGER, LEGAL, EXECUTIVE.", required = true, schema = @Schema(type = "String", allowableValues = {
|
@Parameter(description = "The scale of pages in the output PDF. Acceptable values are A0-A10, B0-B9, LETTER, TABLOID, LEDGER, LEGAL, EXECUTIVE.", required = true, schema = @Schema(type = "string", allowableValues = {
|
||||||
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "B0", "B1", "B2", "B3", "B4",
|
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "B0", "B1", "B2", "B3", "B4",
|
||||||
"B5", "B6", "B7", "B8", "B9", "LETTER", "TABLOID", "LEDGER", "LEGAL",
|
"B5", "B6", "B7", "B8", "B9", "LETTER", "TABLOID", "LEDGER", "LEGAL",
|
||||||
"EXECUTIVE" })) @RequestParam("pageSize") String targetPageSize,
|
"EXECUTIVE" })) @RequestParam("pageSize") String targetPageSize,
|
||||||
@Parameter(description = "The scale of the content on the pages of the output PDF. Acceptable values are floats.", required = true, schema = @Schema(type = "float")) @RequestParam("scaleFactor") float scaleFactor)
|
@Parameter(description = "The scale of the content on the pages of the output PDF. Acceptable values are floats.", required = true, schema = @Schema(type = "integer")) @RequestParam("scaleFactor") float scaleFactor)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
Map<String, PageSize> sizeMap = new HashMap<>();
|
Map<String, PageSize> sizeMap = new HashMap<>();
|
||||||
|
|
|
@ -221,6 +221,15 @@ public class CompressController {
|
||||||
// Read the optimized PDF file
|
// Read the optimized PDF file
|
||||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||||
|
|
||||||
|
// Check if optimized file is larger than the original
|
||||||
|
if(pdfBytes.length > inputFileSize) {
|
||||||
|
// Log the occurrence
|
||||||
|
logger.warn("Optimized file is larger than the original. Returning the original file instead.");
|
||||||
|
|
||||||
|
// Read the original file again
|
||||||
|
pdfBytes = Files.readAllBytes(tempInputFile);
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up the temporary files
|
// Clean up the temporary files
|
||||||
Files.delete(tempInputFile);
|
Files.delete(tempInputFile);
|
||||||
Files.delete(tempOutputFile);
|
Files.delete(tempOutputFile);
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
package stirling.software.SPDF.controller.api.other;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
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 io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
import org.apache.pdfbox.pdmodel.*;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.*;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import io.swagger.v3.oas.annotations.*;
|
||||||
|
import io.swagger.v3.oas.annotations.media.*;
|
||||||
|
import io.swagger.v3.oas.annotations.parameters.*;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
|
import org.apache.tomcat.util.http.ResponseUtil;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
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.PdfReader;
|
||||||
|
import com.itextpdf.kernel.pdf.PdfWriter;
|
||||||
|
import com.itextpdf.kernel.pdf.PdfDocument;
|
||||||
|
import com.itextpdf.kernel.pdf.PdfPage;
|
||||||
|
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.media.Schema;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Tag(name = "Other", description = "Other APIs")
|
||||||
|
public class PageNumbersController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class);
|
||||||
|
|
||||||
|
@PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data")
|
||||||
|
@Operation(summary = "Add page numbers to a PDF document", description = "This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO")
|
||||||
|
public ResponseEntity<byte[]> addPageNumbers(
|
||||||
|
@Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file,
|
||||||
|
@Parameter(description = "Custom margin: small/medium/large", required = true, schema = @Schema(type = "string", allowableValues = {"small", "medium", "large"})) @RequestParam("customMargin") String customMargin,
|
||||||
|
@Parameter(description = "Position: 1 of 9 positions", required = true, schema = @Schema(type = "integer", minimum = "1", maximum = "9")) @RequestParam("position") int position,
|
||||||
|
@Parameter(description = "Starting number", required = true, schema = @Schema(type = "integer", minimum = "1")) @RequestParam("startingNumber") int startingNumber,
|
||||||
|
@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)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
byte[] fileBytes = file.getBytes();
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
|
||||||
|
|
||||||
|
int pageNumber = startingNumber;
|
||||||
|
float marginFactor;
|
||||||
|
switch (customMargin.toLowerCase()) {
|
||||||
|
case "small":
|
||||||
|
marginFactor = 0.01f;
|
||||||
|
break;
|
||||||
|
case "medium":
|
||||||
|
marginFactor = 0.025f;
|
||||||
|
break;
|
||||||
|
case "large":
|
||||||
|
marginFactor = 0.05f;
|
||||||
|
break;
|
||||||
|
case "x-large":
|
||||||
|
marginFactor = 0.1f;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
marginFactor = 0.01f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float fontSize = 12.0f;
|
||||||
|
|
||||||
|
PdfReader reader = new PdfReader(bais);
|
||||||
|
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) {
|
||||||
|
PdfPage page = pdfDoc.getPage(i+1);
|
||||||
|
Rectangle pageSize = page.getPageSize();
|
||||||
|
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
|
||||||
|
|
||||||
|
String text = customText != null ? customText.replace("{n}", String.valueOf(pageNumber)).replace("{p}", String.valueOf(pdfDoc.getNumberOfPages())) : 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;
|
||||||
|
TextAlignment alignment;
|
||||||
|
|
||||||
|
int xGroup = (position - 1) % 3;
|
||||||
|
int yGroup = 2 - (position - 1) / 3;
|
||||||
|
|
||||||
|
switch (xGroup) {
|
||||||
|
case 0: // left
|
||||||
|
x = pageSize.getLeft() + marginFactor * pageSize.getWidth();
|
||||||
|
alignment = TextAlignment.LEFT;
|
||||||
|
break;
|
||||||
|
case 1: // center
|
||||||
|
x = pageSize.getLeft() + (pageSize.getWidth()) / 2;
|
||||||
|
alignment = TextAlignment.CENTER;
|
||||||
|
break;
|
||||||
|
default: // right
|
||||||
|
x = pageSize.getRight() - marginFactor * pageSize.getWidth();
|
||||||
|
alignment = TextAlignment.RIGHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (yGroup) {
|
||||||
|
case 0: // bottom
|
||||||
|
y = pageSize.getBottom() + marginFactor * pageSize.getHeight();
|
||||||
|
break;
|
||||||
|
case 1: // middle
|
||||||
|
y = pageSize.getBottom() + (pageSize.getHeight() ) / 2;
|
||||||
|
break;
|
||||||
|
default: // top
|
||||||
|
y = pageSize.getTop() - marginFactor * pageSize.getHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
new Canvas(pdfCanvas, page.getPageSize())
|
||||||
|
.showTextAligned(new Paragraph(text).setFont(font).setFontSize(fontSize), x, y, alignment);
|
||||||
|
|
||||||
|
pageNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pdfDoc.close();
|
||||||
|
byte[] resultBytes = baos.toByteArray();
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header("Content-Type", "application/pdf; charset=UTF-8")
|
||||||
|
.header("Content-Disposition", "inline; filename=" + URLEncoder.encode(file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf", "UTF-8"))
|
||||||
|
.body(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -32,6 +32,13 @@ public class OtherWebController {
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/add-page-numbers")
|
||||||
|
@Hidden
|
||||||
|
public String addPageNumbersForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "add-page-numbers");
|
||||||
|
return "other/add-page-numbers";
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/extract-images")
|
@GetMapping("/extract-images")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String extractImagesForm(Model model) {
|
public String extractImagesForm(Model model) {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package stirling.software.SPDF.utils;
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -88,4 +92,16 @@ public class GeneralUtils {
|
||||||
|
|
||||||
return newPageOrder;
|
return newPageOrder;
|
||||||
}
|
}
|
||||||
|
public static boolean createDir(String path) {
|
||||||
|
Path folder = Paths.get(path);
|
||||||
|
if (!Files.exists(folder)) {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(folder);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ server.error.whitelabel.enabled=false
|
||||||
server.error.include-stacktrace=always
|
server.error.include-stacktrace=always
|
||||||
server.error.include-exception=true
|
server.error.include-exception=true
|
||||||
server.error.include-message=always
|
server.error.include-message=always
|
||||||
|
\
|
||||||
server.servlet.session.tracking-modes=cookie
|
server.servlet.session.tracking-modes=cookie
|
||||||
server.servlet.context-path=${APP_ROOT_PATH:/}
|
server.servlet.context-path=${APP_ROOT_PATH:/}
|
||||||
|
|
||||||
|
@ -26,3 +26,7 @@ spring.thymeleaf.encoding=UTF-8
|
||||||
|
|
||||||
server.connection-timeout=${CONNECTION_TIMEOUT:5m}
|
server.connection-timeout=${CONNECTION_TIMEOUT:5m}
|
||||||
spring.mvc.async.request-timeout=${ASYNC_CONNECTION_TIMEOUT:300000}
|
spring.mvc.async.request-timeout=${ASYNC_CONNECTION_TIMEOUT:300000}
|
||||||
|
|
||||||
|
spring.resources.static-locations=file:customFiles/static/
|
||||||
|
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
||||||
|
#spring.thymeleaf.cache=false
|
|
@ -132,8 +132,11 @@ home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
|
||||||
home.scalePages.title=Adjust page size/scale
|
home.scalePages.title=Adjust page size/scale
|
||||||
home.scalePages.desc=Change the size/scale of a page and/or its contents.
|
home.scalePages.desc=Change the size/scale of a page and/or its contents.
|
||||||
|
|
||||||
home.pipeline.title=Pipeline
|
home.pipeline.title=Pipeline (Advanced)
|
||||||
home.pipeline.desc=Pipeline desc.
|
home.pipeline.desc=Run multiple actions on PDFs by defining pipeline scripts
|
||||||
|
|
||||||
|
home.add-page-numbers.title=Add Page Numbers
|
||||||
|
home.add-page-numbers.desc=Add Page numbers throughout a document in a set location
|
||||||
|
|
||||||
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
|
||||||
|
|
||||||
|
|
3
src/main/resources/static/images/add-page-numbers.svg
Normal file
3
src/main/resources/static/images/add-page-numbers.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-123" viewBox="0 0 16 16">
|
||||||
|
<path d="M2.873 11.297V4.142H1.699L0 5.379v1.137l1.64-1.18h.06v5.961h1.174Zm3.213-5.09v-.063c0-.618.44-1.169 1.196-1.169.676 0 1.174.44 1.174 1.106 0 .624-.42 1.101-.807 1.526L4.99 10.553v.744h4.78v-.99H6.643v-.069L8.41 8.252c.65-.724 1.237-1.332 1.237-2.27C9.646 4.849 8.723 4 7.308 4c-1.573 0-2.36 1.064-2.36 2.15v.057h1.138Zm6.559 1.883h.786c.823 0 1.374.481 1.379 1.179.01.707-.55 1.216-1.421 1.21-.77-.005-1.326-.419-1.379-.953h-1.095c.042 1.053.938 1.918 2.464 1.918 1.478 0 2.642-.839 2.62-2.144-.02-1.143-.922-1.651-1.551-1.714v-.063c.535-.09 1.347-.66 1.326-1.678-.026-1.053-.933-1.855-2.359-1.845-1.5.005-2.317.88-2.348 1.898h1.116c.032-.498.498-.944 1.206-.944.703 0 1.206.435 1.206 1.07.005.64-.504 1.106-1.2 1.106h-.75v.96Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 870 B |
|
@ -13,7 +13,7 @@
|
||||||
<div class="container ">
|
<div class="container ">
|
||||||
|
|
||||||
<a class="navbar-brand" href="#" th:href="@{/}" >
|
<a class="navbar-brand" href="#" th:href="@{/}" >
|
||||||
<img th:if="${@navBarText} == 'Stirling PDF'" class="main-icon" src="favicon.svg" alt="icon">
|
<img class="main-icon" src="favicon.svg" alt="icon">
|
||||||
<span class="icon-text" th:text="${@navBarText}"></span>
|
<span class="icon-text" th:text="${@navBarText}"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@
|
||||||
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('repair', 'images/wrench.svg', 'home.repair.title', 'home.repair.desc')}"></div>
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('repair', 'images/wrench.svg', 'home.repair.title', 'home.repair.desc')}"></div>
|
||||||
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-blanks', 'images/blank-file.svg', 'home.removeBlanks.title', 'home.removeBlanks.desc')}"></div>
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-blanks', 'images/blank-file.svg', 'home.removeBlanks.title', 'home.removeBlanks.desc')}"></div>
|
||||||
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('compare', 'images/scales.svg', 'home.compare.title', 'home.compare.desc')}"></div>
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('compare', 'images/scales.svg', 'home.compare.title', 'home.compare.desc')}"></div>
|
||||||
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('compare', 'images/add-page-numbers.svg', 'home.add-page-numbers.title', 'home.add-page-numbers.desc')}"></div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<!-- Features -->
|
<!-- Features -->
|
||||||
<div class="features-container container">
|
<div class="features-container container">
|
||||||
|
|
||||||
<!-- <div th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', svgPath='images/pipeline.svg')}"></div>-->
|
<div th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', svgPath='images/pipeline.svg')}"></div>
|
||||||
|
|
||||||
<div th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', svgPath='images/tools.svg')}"></div>
|
<div th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', svgPath='images/tools.svg')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(id='merge-pdfs', cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs', svgPath='images/union.svg')}"></div>
|
<div th:replace="~{fragments/card :: card(id='merge-pdfs', cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs', svgPath='images/union.svg')}"></div>
|
||||||
|
@ -68,6 +68,10 @@
|
||||||
<div th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', svgPath='images/page-layout.svg')}"></div>
|
<div th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', svgPath='images/page-layout.svg')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(id='scale-pages', cardTitle=#{home.scalePages.title}, cardText=#{home.scalePages.desc}, cardLink='scale-pages', svgPath='images/scale-pages.svg')}"></div>
|
<div th:replace="~{fragments/card :: card(id='scale-pages', cardTitle=#{home.scalePages.title}, cardText=#{home.scalePages.desc}, cardLink='scale-pages', svgPath='images/scale-pages.svg')}"></div>
|
||||||
|
|
||||||
|
<div th:replace="~{fragments/card :: card(id='add-page-numbers', cardTitle=#{home.add-page-numbers.title}, cardText=#{home.add-page-numbers.desc}, cardLink='add-page-numbers', svgPath='images/add-page-numbers.svg')}"></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="js/homecard.js"></script>
|
<script src="js/homecard.js"></script>
|
||||||
|
|
51
src/main/resources/templates/other/add-page-numbers.html
Normal file
51
src/main/resources/templates/other/add-page-numbers.html
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<th:block th:insert="~{fragments/common :: head(title=#{autoCrop.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2 th:text="#{addPageNumbers.header}"></h2>
|
||||||
|
<form method="post" enctype="multipart/form-data" th:action="@{add-page-numbers}">
|
||||||
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||||
|
<br>
|
||||||
|
<label for="customMargin">Margin Size</label>
|
||||||
|
<select id="customMargin" name="customMargin" required>
|
||||||
|
<option value="" disabled selected>Select a margin size</option>
|
||||||
|
<option value="small">Small</option>
|
||||||
|
<option value="medium">Medium</option>
|
||||||
|
<option value="large">Large</option>
|
||||||
|
</select>
|
||||||
|
<br>
|
||||||
|
<label for="position">Position</label>
|
||||||
|
<input type="number" id="position" name="position" placeholder="Position: 1 of 9 positions" min="1" max="9" required/>
|
||||||
|
<br>
|
||||||
|
<label for="startingNumber">Starting Number</label>
|
||||||
|
<input type="number" id="startingNumber" name="startingNumber" placeholder="Starting number" min="1" required/>
|
||||||
|
<br>
|
||||||
|
<label for="pagesToNumber">Pages to Number</label>
|
||||||
|
<input type="text" id="pagesToNumber" name="pagesToNumber" placeholder="Which pages to number, default all"/>
|
||||||
|
<br>
|
||||||
|
<label for="customText">Custom Text</label>
|
||||||
|
<input type="text" id="customText" name="customText" placeholder="Custom text: defaults to just number but can have things like 'Page {n} of {p}'"/>
|
||||||
|
<br>
|
||||||
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPageNumbers.submit}"></button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue