Compare commits

...

33 Commits

Author SHA1 Message Date
Anthony Stirling
2fe9b5a24b Merge pull request #699 from Stirling-Tools/pdfbox3
PDFBox v3 upgrade plus resolution to reversed text
2024-01-13 10:13:00 +00:00
Anthony Stirling
3912f42128 changeCreds message 2024-01-13 10:08:42 +00:00
Anthony Stirling
801e307005 0.20 bump 2024-01-13 01:09:41 +00:00
Anthony Stirling
c8acddb251 Resolve split sections 2024-01-13 01:05:43 +00:00
Anthony Stirling
d8cf7e81b9 fixes 2024-01-13 00:55:43 +00:00
Anthony Stirling
c4ad442ec3 remove logs 2024-01-13 00:46:17 +00:00
Anthony Stirling
c8e5023ec1 fix 2024-01-13 00:37:19 +00:00
Anthony Stirling
5281d7a49a pdfbox3 upgrade and fix 2024-01-12 23:15:27 +00:00
Anthony Stirling
77dcf04cfe Merge pull request #697 from Stirling-Tools/contributing
docs: add contributing guide
2024-01-11 23:07:37 +00:00
sbplat
b6523e9989 Merge branch 'contributing' of https://github.com/Stirling-Tools/Stirling-PDF into contributing 2024-01-11 17:39:22 -05:00
sbplat
d52a00185b Merge branch 'main' into contributing 2024-01-11 17:39:03 -05:00
sbplat
00487275a7 docs: list supported languages in readme 2024-01-11 17:38:05 -05:00
Anthony Stirling
823c8eb53e Merge pull request #686 from PeterDaveHelloKitchen/Markdown
Specify code block language in Markdown for syntax highlighting
2024-01-11 22:38:02 +00:00
Anthony Stirling
e9b8981a35 Merge pull request #698 from Stirling-Tools/Sf298-patch-1
Updated demo site
2024-01-11 22:30:10 +00:00
Saud Fatayerji
575e0b3e54 Updated demo site 2024-01-11 22:07:56 +03:00
sbplat
db931717a1 Merge branch 'main' into contributing 2024-01-11 10:47:55 -05:00
sbplat
787c59efd3 Merge pull request #694 from iLern/bugfix-md2pdf
Enable support for tables in the conversion from Markdown to HTML
2024-01-11 10:47:40 -05:00
sbplat
45aead89e3 docs: add contributing guide 2024-01-11 10:35:34 -05:00
TieStone
81d49b722b add table support in md2pdf transport 2024-01-11 14:54:01 +08:00
TieStone
ab9e7bbb8c add table support in md2pdf transport 2024-01-11 14:54:01 +08:00
TieStone
ee223d0405 add table support 2024-01-11 14:54:01 +08:00
Stirling-PDF-Bot
99050ad73e Update 3rd Party Licenses 2024-01-11 14:40:26 +08:00
sbplat
9fc873e973 Merge pull request #683 from Stirling-Tools/version_fix
fix: version showing as 0.0.0
2024-01-10 09:28:43 -05:00
Peter Dave Hello
52fe4c6aa6 Specify code block language in Markdown for syntax highlighting 2024-01-10 19:17:09 +08:00
sbplat
66df7053bb Merge branch 'main' into version_fix 2024-01-09 21:17:57 -05:00
sbplat
edde1a6436 fix: version showing as 0.0.0 2024-01-09 21:13:35 -05:00
Anthony Stirling
50ee829e5f Merge pull request #682 from Stirling-Tools/ebook
Ebook
2024-01-10 00:41:54 +00:00
Anthony Stirling
1924dfb4f1 Merge branch 'main' into ebook 2024-01-10 00:40:17 +00:00
Anthony Stirling
873a4ecb7e revert 2024-01-10 00:39:26 +00:00
Anthony Stirling
32da14acbf log removal 2024-01-10 00:37:55 +00:00
Anthony Stirling
139c793b5e 0.19.1 2024-01-10 00:33:22 +00:00
Anthony Stirling
e717d83f75 fixes and timeouts 2024-01-10 00:33:07 +00:00
Anthony Stirling
ef12c2f892 Add ebook support 2024-01-09 22:39:21 +00:00
67 changed files with 1011 additions and 230 deletions

40
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,40 @@
# Contributing to Stirling-PDF
Thank you for your interest in contributing to Stirling-PDF! There are many ways to contribute other than writing code. For example, reporting bugs, creating suggestions, and adding or modifying translations.
## Issue Guidelines
Issues can be used to report bugs, request features, or ask questions. If you have a question, you could also ask us in our [Discord](https://discord.gg/FJUSXUSYec).
Before opening an issue, please check to make sure someone hasn't already opened an issue about it.
## Pull Requests
Before you start working on an issue, please comment on (or create) the issue and wait for it to be assigned to you. If someone has already been assigned but didn't have the time to work on it lately, please communicate with them and ask if they're still working on it. This is to avoid multiple people working on the same issue.
Once you have been assigned an issue, you can start working on it. When you are ready to submit your changes, open a pull request.
For a detailed pull request tutorial, see [this guide](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github).
Please make sure your Pull Request adheres to the following guidelines:
- Use the PR template provided.
- Keep your Pull Request title succinct, detailed and to the point.
- Keep commits atomic. One commit should contain one change. If you want to make multiple changes, submit multiple Pull Requests.
- Commits should be clear, concise and easy to understand.
- References to the Issue number in the Pull Request and/or Commit message.
## Translations
If you would like to add or modify a translation, please see [How to add new languages to Stirling-PDF](HowToAddNewLanguage.md). Also, please create a Pull Request so others can use it!
## Fixing Bugs or Adding a New Feature
First, make sure you've read the section [Pull Requests](#pull-requests).
To build from source, please follow this [Guide](LocalRunGuide.md).
If, at any point of time, you have a question, please feel free to ask in the same issue thread or in our [Discord](https://discord.gg/FJUSXUSYec).
## License
By contributing to this project, you agree that your contributions will be licensed under the [GPL 3 License](LICENSE). You also acknowledge and agree that your contributions will be included in Stirling-PDF and that they can be relicensed in the future under the MPL 2.0 (Mozilla Public License Version 2.0) license.

View File

@@ -2,7 +2,7 @@
### Whilst Pipelines are in alpha... ### Whilst Pipelines are in alpha...
You must enable this alpha functionality by setting You must enable this alpha functionality by setting
``` ```yaml
system: system:
enableAlphaFunctionality: true enableAlphaFunctionality: true
``` ```

View File

@@ -16,7 +16,7 @@ If your language isnt represented by a flag just find whichever closely matches
For example to add Polish you would add For example to add Polish you would add
``` ```html
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL"> <a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski <img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
</a> </a>

View File

@@ -2,7 +2,7 @@
## Whilst Pipelines are in alpha... ## Whilst Pipelines are in alpha...
You must enable this alpha functionality by setting You must enable this alpha functionality by setting
``` ```yaml
system: system:
enableAlphaFunctionality: true enableAlphaFunctionality: true
``` ```

123
README.md
View File

@@ -16,15 +16,13 @@ Stirling PDF makes no outbound calls for any record keeping or tracking.
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point. All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
Please feel free to submit feature requests or report bugs either through GitHub issues or on our [Discord](https://discord.gg/Cn8pWhQRxZ)
![stirling-home](images/stirling-home.png) ![stirling-home](images/stirling-home.png)
## Features ## Features
- Dark mode support. - Dark mode support.
- Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example) - Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example)
- Parallel file processing and downloads - Parallel file processing and downloads
- API for integration with external scripts - API for integration with external scripts
- Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation) - Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation)
@@ -32,56 +30,56 @@ Please feel free to submit feature requests or report bugs either through GitHub
### **Page Operations** ### **Page Operations**
- View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts) - View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts)
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages. - Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
- Merge multiple PDFs together into a single resultant file. - Merge multiple PDFs together into a single resultant file.
- Split PDFs into multiple files at specified page numbers or extract all pages as individual files. - Split PDFs into multiple files at specified page numbers or extract all pages as individual files.
- Reorganize PDF pages into different orders. - Reorganize PDF pages into different orders.
- Rotate PDFs in 90-degree increments. - Rotate PDFs in 90-degree increments.
- Remove pages. - Remove pages.
- Multi-page layout (Format PDFs into a multi-paged page). - Multi-page layout (Format PDFs into a multi-paged page).
- Scale page contents size by set %. - Scale page contents size by set %.
- Adjust Contrast. - Adjust Contrast.
- Crop PDF. - Crop PDF.
- Auto Split PDF (With physically scanned page dividers). - Auto Split PDF (With physically scanned page dividers).
- Extract page(s). - Extract page(s).
- Convert PDF to a single page. - Convert PDF to a single page.
### **Conversion Operations** ### **Conversion Operations**
- Convert PDFs to and from images. - Convert PDFs to and from images.
- Convert any common file to PDF (using LibreOffice). - Convert any common file to PDF (using LibreOffice).
- Convert PDF to Word/Powerpoint/Others (using LibreOffice). - Convert PDF to Word/Powerpoint/Others (using LibreOffice).
- Convert HTML to PDF. - Convert HTML to PDF.
- URL to PDF. - URL to PDF.
- Markdown to PDF. - Markdown to PDF.
### **Security & Permissions** ### **Security & Permissions**
- Add and remove passwords. - Add and remove passwords.
- Change/set PDF Permissions. - Change/set PDF Permissions.
- Add watermark(s). - Add watermark(s).
- Certify/sign PDFs. - Certify/sign PDFs.
- Sanitize PDFs. - Sanitize PDFs.
- Auto-redact text. - Auto-redact text.
### **Other Operations** ### **Other Operations**
- Add/Generate/Write signatures. - Add/Generate/Write signatures.
- Repair PDFs. - Repair PDFs.
- Detect and remove blank pages. - Detect and remove blank pages.
- Compare 2 PDFs and show differences in text. - Compare 2 PDFs and show differences in text.
- Add images to PDFs. - Add images to PDFs.
- Compress PDFs to decrease their filesize (Using OCRMyPDF). - Compress PDFs to decrease their filesize (Using OCRMyPDF).
- Extract images from PDF. - Extract images from PDF.
- Extract images from Scans. - Extract images from Scans.
- Add page numbers. - Add page numbers.
- Auto rename file by detecting PDF header text. - Auto rename file by detecting PDF header text.
- OCR on PDF (Using OCRMyPDF). - OCR on PDF (Using OCRMyPDF).
- PDF/A conversion (Using OCRMyPDF). - PDF/A conversion (Using OCRMyPDF).
- Edit metadata. - Edit metadata.
- Flatten PDFs. - Flatten PDFs.
- Get all information on a PDF to view or export as JSON. - Get all information on a PDF to view or export as JSON.
For a overview of the tasks and the technology each uses please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md) For a overview of the tasks and the technology each uses please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md)
Hosted instance/demo of the app can be seen [here](https://pdf.adminforge.de/) hosted by the team at adminforge.de Demo of the app is available [here](https://stirlingpdf.io). username: demo, password: demo
## Technologies used ## Technologies used
- Spring Boot + Thymeleaf - Spring Boot + Thymeleaf
@@ -109,7 +107,7 @@ For people that don't mind about space optimization just use the latest tag.
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-ultra-lite?label=Stirling-PDF%20Ultra-Lite) ![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-ultra-lite?label=Stirling-PDF%20Ultra-Lite)
Docker Run Docker Run
``` ```bash
docker run -d \ docker run -d \
-p 8080:8080 \ -p 8080:8080 \
-v /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata \ -v /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata \
@@ -118,14 +116,14 @@ docker run -d \
-e DOCKER_ENABLE_SECURITY=false \ -e DOCKER_ENABLE_SECURITY=false \
--name stirling-pdf \ --name stirling-pdf \
frooodle/s-pdf:latest frooodle/s-pdf:latest
Can also add these for customisation but are not required Can also add these for customisation but are not required
-v /location/of/customFiles:/customFiles \ -v /location/of/customFiles:/customFiles \
``` ```
Docker Compose Docker Compose
``` ```yaml
version: '3.3' version: '3.3'
services: services:
stirling-pdf: stirling-pdf:
@@ -146,7 +144,8 @@ Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "pod
## Enable OCR/Compression feature ## Enable OCR/Compression feature
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md
## Want to add your own language? ## Supported Languages
Stirling PDF currently supports 26! Stirling PDF currently supports 26!
- English (English) (en_GB) - English (English) (en_GB)
- English (US) (en_US) - English (US) (en_US)
@@ -175,15 +174,9 @@ Stirling PDF currently supports 26!
- Bulgarian (Български) (bg_BG) - Bulgarian (Български) (bg_BG)
- Sebian Latin alphabet (Srpski) (sr-Latn-RS) - Sebian Latin alphabet (Srpski) (sr-Latn-RS)
If you want to add your own language to Stirling-PDF please refer ## Contributing (creating issues, translations, fixing bugs, etc.)
https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md
And please create a PR to merge it back in so others can use it!
## How to View
1. Open a web browser and navigate to `http://localhost:8080/`
2. Use the application by following the instructions on the website.
Please see our [Contributing Guide](CONTRIBUTING.md)!
## Customisation ## Customisation
Stirling PDF allows easy customization of the app. Stirling PDF allows easy customization of the app.
@@ -197,7 +190,7 @@ This file is located in the ``/configs`` directory and follows standard YAML for
Environment variables are also supported and would override the settings file Environment variables are also supported and would override the settings file
For example in the settings.yml you have For example in the settings.yml you have
``` ```yaml
system: system:
defaultLocale: 'en-US' defaultLocale: 'en-US'
``` ```
@@ -205,7 +198,7 @@ system:
To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE`` To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE``
The Current list of settings is The Current list of settings is
``` ```yaml
security: security:
enableLogin: false # set to 'true' to enable login enableLogin: false # set to 'true' to enable login
csrfDisabled: true csrfDisabled: true
@@ -243,9 +236,9 @@ For those wanting to use Stirling-PDFs backend API to link with their own custom
## Login authentication ## Login authentication
![stirling-login](images/login-light.png) ![stirling-login](images/login-light.png)
### Prerequisites: ### Prerequisites:
- User must have the folder ./configs volumed within docker so that it is retained during updates. - User must have the folder ./configs volumed within docker so that it is retained during updates.
- Docker uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables. - Docker uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables.
- Then either enable login via the settings.yml file or via setting ``SECURITY_ENABLE_LOGIN`` to ``true`` - Then either enable login via the settings.yml file or via setting ``SECURITY_ENABLE_LOGIN`` to ``true``
- Now the initial user will be generated with username ``admin`` and password ``stirling``. On login you will be forced to change the password to a new one. You can also use the environment variables ``SECURITY_INITIALLOGIN_USERNAME`` and ``SECURITY_INITIALLOGIN_PASSWORD`` to set your own straight away (Recommended to remove them after user creation). - Now the initial user will be generated with username ``admin`` and password ``stirling``. On login you will be forced to change the password to a new one. You can also use the environment variables ``SECURITY_INITIALLOGIN_USERNAME`` and ``SECURITY_INITIALLOGIN_PASSWORD`` to set your own straight away (Recommended to remove them after user creation).
@@ -266,10 +259,10 @@ For API usage you must provide a header with 'X-API-Key' and the associated API
- Progress bar/Tracking - Progress bar/Tracking
- Full custom logic pipelines to combine multiple operations together. - Full custom logic pipelines to combine multiple operations together.
- Folder support with auto scanning to perform operations on - Folder support with auto scanning to perform operations on
- Redact text (Via UI not just automated way) - Redact text (Via UI not just automated way)
- Add Forms - Add Forms
- Multi page layout (Stich PDF pages together) support x rows y columns and custom page sizing - Multi page layout (Stich PDF pages together) support x rows y columns and custom page sizing
- Fill forms mannual and automatic - Fill forms mannual and automatic
### Q2: Why is my application downloading .htm files? ### Q2: Why is my application downloading .htm files?
This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files. This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files.

View File

@@ -12,7 +12,7 @@ plugins {
import com.github.jk1.license.render.* import com.github.jk1.license.render.*
group = 'stirling.software' group = 'stirling.software'
version = '0.19.0' version = '0.20.0'
sourceCompatibility = '17' sourceCompatibility = '17'
repositories { repositories {
@@ -137,11 +137,11 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'commons-logging', module: 'commons-logging'
} }
implementation ('org.apache.pdfbox:pdfbox:2.0.30'){ implementation ('org.apache.pdfbox:pdfbox:3.0.1'){
exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'commons-logging', module: 'commons-logging'
} }
implementation ('org.apache.pdfbox:xmpbox:2.0.30'){ implementation ('org.apache.pdfbox:xmpbox:3.0.1'){
exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'commons-logging', module: 'commons-logging'
} }
@@ -152,6 +152,7 @@ dependencies {
implementation group: 'com.google.zxing', name: 'core', version: '3.5.2' implementation group: 'com.google.zxing', name: 'core', version: '3.5.2'
// https://mvnrepository.com/artifact/org.commonmark/commonmark // https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation 'org.commonmark:commonmark:0.21.0' implementation 'org.commonmark:commonmark:0.21.0'
implementation 'org.commonmark:commonmark-ext-gfm-tables:0.21.0'
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core // https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0' implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
@@ -163,6 +164,9 @@ dependencies {
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
dependsOn 'spotlessApply' dependsOn 'spotlessApply'
} }
compileJava {
options.compilerArgs << '-parameters'
}
task writeVersion { task writeVersion {
def propsFile = file('src/main/resources/version.properties') def propsFile = file('src/main/resources/version.properties')

View File

@@ -1,8 +1,15 @@
package stirling.software.SPDF.config; package stirling.software.SPDF.config;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
@@ -24,8 +31,15 @@ public class AppConfig {
@Bean(name = "appVersion") @Bean(name = "appVersion")
public String appVersion() { public String appVersion() {
String version = getClass().getPackage().getImplementationVersion(); Resource resource = new ClassPathResource("version.properties");
return (version != null) ? version : "0.0.0"; Properties props = new Properties();
try {
props.load(resource.getInputStream());
return props.getProperty("version");
} catch (IOException e) {
e.printStackTrace();
}
return "0.0.0";
} }
@Bean(name = "homeText") @Bean(name = "homeText")
@@ -57,4 +71,22 @@ public class AppConfig {
if (appName == null) appName = System.getenv("rateLimit"); if (appName == null) appName = System.getenv("rateLimit");
return (appName != null) ? Boolean.valueOf(appName) : false; return (appName != null) ? Boolean.valueOf(appName) : false;
} }
@Bean(name = "RunningInDocker")
public boolean runningInDocker() {
return Files.exists(Paths.get("/.dockerenv"));
}
@Bean(name = "bookFormatsInstalled")
public boolean bookFormatsInstalled() {
return applicationProperties.getSystem().getCustomApplications().isInstallBookFormats();
}
@Bean(name = "htmlFormatsInstalled")
public boolean htmlFormatsInstalled() {
return applicationProperties
.getSystem()
.getCustomApplications()
.isInstallAdvancedHtmlToPDF();
}
} }

View File

@@ -9,11 +9,14 @@ import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
@Service @Service
@DependsOn({"bookFormatsInstalled"})
public class EndpointConfiguration { public class EndpointConfiguration {
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class); private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>(); private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
@@ -21,9 +24,14 @@ public class EndpointConfiguration {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private boolean bookFormatsInstalled;
@Autowired @Autowired
public EndpointConfiguration(ApplicationProperties applicationProperties) { public EndpointConfiguration(
ApplicationProperties applicationProperties,
@Qualifier("bookFormatsInstalled") boolean bookFormatsInstalled) {
this.applicationProperties = applicationProperties; this.applicationProperties = applicationProperties;
this.bookFormatsInstalled = bookFormatsInstalled;
init(); init();
processEnvironmentConfigs(); processEnvironmentConfigs();
} }
@@ -145,6 +153,12 @@ public class EndpointConfiguration {
addEndpointToGroup("CLI", "ocr-pdf"); addEndpointToGroup("CLI", "ocr-pdf");
addEndpointToGroup("CLI", "html-to-pdf"); addEndpointToGroup("CLI", "html-to-pdf");
addEndpointToGroup("CLI", "url-to-pdf"); addEndpointToGroup("CLI", "url-to-pdf");
addEndpointToGroup("CLI", "book-to-pdf");
addEndpointToGroup("CLI", "pdf-to-book");
// Calibre
addEndpointToGroup("Calibre", "book-to-pdf");
addEndpointToGroup("Calibre", "pdf-to-book");
// python // python
addEndpointToGroup("Python", "extract-image-scans"); addEndpointToGroup("Python", "extract-image-scans");
@@ -215,7 +229,9 @@ public class EndpointConfiguration {
private void processEnvironmentConfigs() { private void processEnvironmentConfigs() {
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove(); List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove(); List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
if (!bookFormatsInstalled) {
groupsToRemove.add("Calibre");
}
if (endpointsToRemove != null) { if (endpointsToRemove != null) {
for (String endpoint : endpointsToRemove) { for (String endpoint : endpointsToRemove) {
disableEndpoint(endpoint.trim()); disableEndpoint(endpoint.trim());

View File

@@ -0,0 +1,98 @@
package stirling.software.SPDF.config;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
@Component
public class PostStartupProcesses {
@Autowired ApplicationProperties applicationProperties;
@Autowired
@Qualifier("RunningInDocker")
private boolean runningInDocker;
@Autowired
@Qualifier("bookFormatsInstalled")
private boolean bookFormatsInstalled;
@Autowired
@Qualifier("htmlFormatsInstalled")
private boolean htmlFormatsInstalled;
private static final Logger logger = LoggerFactory.getLogger(PostStartupProcesses.class);
@PostConstruct
public void runInstallCommandBasedOnEnvironment() throws IOException, InterruptedException {
List<List<String>> commands = new ArrayList<>();
// Checking for DOCKER_INSTALL_BOOK_FORMATS environment variable
if (bookFormatsInstalled) {
List<String> tmpList = new ArrayList<>();
// Set up the timezone configuration commands
tmpList.addAll(
Arrays.asList(
"sh",
"-c",
"echo 'tzdata tzdata/Areas select Europe' | debconf-set-selections; "
+ "echo 'tzdata tzdata/Zones/Europe select Berlin' | debconf-set-selections"));
commands.add(tmpList);
// Install calibre with DEBIAN_FRONTEND set to noninteractive
tmpList = new ArrayList<>();
tmpList.addAll(
Arrays.asList(
"sh",
"-c",
"DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends calibre"));
commands.add(tmpList);
}
// Checking for DOCKER_INSTALL_HTML_FORMATS environment variable
if (htmlFormatsInstalled) {
List<String> tmpList = new ArrayList<>();
// Add -y flag for automatic yes to prompts and --no-install-recommends to reduce size
tmpList.addAll(
Arrays.asList(
"apt-get", "install", "wkhtmltopdf", "-y", "--no-install-recommends"));
commands.add(tmpList);
}
if (!commands.isEmpty()) {
// Run the command
if (runningInDocker) {
List<String> tmpList = new ArrayList<>();
tmpList.addAll(Arrays.asList("apt-get", "update"));
commands.add(0, tmpList);
for (List<String> list : commands) {
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.INSTALL_APP, true)
.runCommandWithOutputHandling(list);
logger.info("RC for app installs {}", returnCode.getRc());
}
} else {
logger.info(
"Not running inside Docker so skipping automated install process with command.");
}
} else {
if (runningInDocker) {
logger.info("No custom apps to install.");
}
}
}
}

View File

@@ -115,4 +115,4 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
return false; return false;
} }
} }

View File

@@ -1,13 +1,14 @@
package stirling.software.SPDF.controller.api; package stirling.software.SPDF.controller.api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.LayerUtility; import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -37,9 +38,7 @@ public class CropController {
description = description =
"This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO") "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> cropPdf(@ModelAttribute CropPdfForm form) throws IOException { public ResponseEntity<byte[]> cropPdf(@ModelAttribute CropPdfForm form) throws IOException {
PDDocument sourceDocument = Loader.loadPDF(form.getFileInput().getBytes());
PDDocument sourceDocument =
PDDocument.load(new ByteArrayInputStream(form.getFileInput().getBytes()));
PDDocument newDocument = new PDDocument(); PDDocument newDocument = new PDDocument();
@@ -53,7 +52,8 @@ public class CropController {
// Create a new page with the size of the source page // Create a new page with the size of the source page
PDPage newPage = new PDPage(sourcePage.getMediaBox()); PDPage newPage = new PDPage(sourcePage.getMediaBox());
newDocument.addPage(newPage); newDocument.addPage(newPage);
PDPageContentStream contentStream = new PDPageContentStream(newDocument, newPage); PDPageContentStream contentStream =
new PDPageContentStream(newDocument, newPage, AppendMode.OVERWRITE, true, true);
// Import the source page as a form XObject // Import the source page as a form XObject
PDFormXObject formXObject = layerUtility.importPageAsForm(sourceDocument, i); PDFormXObject formXObject = layerUtility.importPageAsForm(sourceDocument, i);

View File

@@ -1,16 +1,17 @@
package stirling.software.SPDF.controller.api; package stirling.software.SPDF.controller.api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.PDFMergerUtility; import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
@@ -27,6 +28,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.general.MergePdfsRequest; import stirling.software.SPDF.model.api.general.MergePdfsRequest;
import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -84,8 +86,8 @@ public class MergeController {
}; };
case "byPDFTitle": case "byPDFTitle":
return (file1, file2) -> { return (file1, file2) -> {
try (PDDocument doc1 = PDDocument.load(file1.getInputStream()); try (PDDocument doc1 = Loader.loadPDF(file1.getBytes());
PDDocument doc2 = PDDocument.load(file2.getInputStream())) { PDDocument doc2 = Loader.loadPDF(file2.getBytes())) {
String title1 = doc1.getDocumentInformation().getTitle(); String title1 = doc1.getDocumentInformation().getTitle();
String title2 = doc2.getDocumentInformation().getTitle(); String title2 = doc2.getDocumentInformation().getTitle();
return title1.compareTo(title2); return title1.compareTo(title2);
@@ -106,6 +108,7 @@ public class MergeController {
"This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO") "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO")
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form) public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
throws IOException { throws IOException {
List<File> filesToDelete = new ArrayList<File>();
try { try {
MultipartFile[] files = form.getFileInput(); MultipartFile[] files = form.getFileInput();
Arrays.sort(files, getSortComparator(form.getSortType())); Arrays.sort(files, getSortComparator(form.getSortType()));
@@ -113,20 +116,27 @@ public class MergeController {
PDFMergerUtility mergedDoc = new PDFMergerUtility(); PDFMergerUtility mergedDoc = new PDFMergerUtility();
ByteArrayOutputStream docOutputstream = new ByteArrayOutputStream(); ByteArrayOutputStream docOutputstream = new ByteArrayOutputStream();
for (MultipartFile file : files) { for (MultipartFile multipartFile : files) {
mergedDoc.addSource(new ByteArrayInputStream(file.getBytes())); File tempFile = GeneralUtils.convertMultipartFileToFile(multipartFile);
filesToDelete.add(tempFile);
mergedDoc.addSource(tempFile);
} }
mergedDoc.setDestinationFileName( mergedDoc.setDestinationFileName(
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf"); files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
mergedDoc.setDestinationStream(docOutputstream); mergedDoc.setDestinationStream(docOutputstream);
mergedDoc.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
mergedDoc.mergeDocuments(null);
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
docOutputstream.toByteArray(), mergedDoc.getDestinationFileName()); docOutputstream.toByteArray(), mergedDoc.getDestinationFileName());
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Error in merge pdf process", ex); logger.error("Error in merge pdf process", ex);
throw ex; throw ex;
} finally {
for (File file : filesToDelete) {
file.delete();
}
} }
} }
} }

View File

@@ -4,6 +4,7 @@ import java.awt.Color;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.LayerUtility; import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
@@ -57,7 +58,7 @@ public class MultiPageLayoutController {
: (int) Math.sqrt(pagesPerSheet); : (int) Math.sqrt(pagesPerSheet);
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet); int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
PDDocument sourceDocument = PDDocument.load(file.getInputStream()); PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
PDDocument newDocument = new PDDocument(); PDDocument newDocument = new PDDocument();
PDPage newPage = new PDPage(PDRectangle.A4); PDPage newPage = new PDPage(PDRectangle.A4);
newDocument.addPage(newPage); newDocument.addPage(newPage);

View File

@@ -8,6 +8,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.Overlay; import org.apache.pdfbox.multipdf.Overlay;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@@ -53,7 +54,7 @@ public class PdfOverlayController {
// "FixedRepeatOverlay" // "FixedRepeatOverlay"
int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode
try (PDDocument basePdf = PDDocument.load(baseFile.getInputStream()); try (PDDocument basePdf = Loader.loadPDF(baseFile.getBytes());
Overlay overlay = new Overlay()) { Overlay overlay = new Overlay()) {
Map<Integer, String> overlayGuide = Map<Integer, String> overlayGuide =
prepareOverlayGuide( prepareOverlayGuide(
@@ -131,7 +132,7 @@ public class PdfOverlayController {
overlayFileIndex = (overlayFileIndex + 1) % overlayFiles.length; overlayFileIndex = (overlayFileIndex + 1) % overlayFiles.length;
} }
try (PDDocument overlayPdf = PDDocument.load(overlayFiles[overlayFileIndex])) { try (PDDocument overlayPdf = Loader.loadPDF(overlayFiles[overlayFileIndex])) {
PDDocument singlePageDocument = new PDDocument(); PDDocument singlePageDocument = new PDDocument();
singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay)); singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay));
File tempFile = File.createTempFile("overlay-page-", ".pdf"); File tempFile = File.createTempFile("overlay-page-", ".pdf");
@@ -147,7 +148,7 @@ public class PdfOverlayController {
} }
private int getNumberOfPages(File file) throws IOException { private int getNumberOfPages(File file) throws IOException {
try (PDDocument doc = PDDocument.load(file)) { try (PDDocument doc = Loader.loadPDF(file)) {
return doc.getNumberOfPages(); return doc.getNumberOfPages();
} }
} }
@@ -159,7 +160,7 @@ public class PdfOverlayController {
File overlayFile = overlayFiles[(basePageIndex - 1) % overlayFiles.length]; File overlayFile = overlayFiles[(basePageIndex - 1) % overlayFiles.length];
// Load the overlay document to check its page count // Load the overlay document to check its page count
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) { try (PDDocument overlayPdf = Loader.loadPDF(overlayFile)) {
int overlayPageCount = overlayPdf.getNumberOfPages(); int overlayPageCount = overlayPdf.getNumberOfPages();
if ((basePageIndex - 1) % overlayPageCount < overlayPageCount) { if ((basePageIndex - 1) % overlayPageCount < overlayPageCount) {
overlayGuide.put(basePageIndex, overlayFile.getAbsolutePath()); overlayGuide.put(basePageIndex, overlayFile.getAbsolutePath());
@@ -181,7 +182,7 @@ public class PdfOverlayController {
int repeatCount = counts[i]; int repeatCount = counts[i];
// Load the overlay document to check its page count // Load the overlay document to check its page count
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) { try (PDDocument overlayPdf = Loader.loadPDF(overlayFile)) {
int overlayPageCount = overlayPdf.getNumberOfPages(); int overlayPageCount = overlayPdf.getNumberOfPages();
for (int j = 0; j < repeatCount; j++) { for (int j = 0; j < repeatCount; j++) {
for (int page = 0; page < overlayPageCount; page++) { for (int page = 0; page < overlayPageCount; page++) {

View File

@@ -4,6 +4,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -42,7 +43,7 @@ public class RearrangePagesPDFController {
MultipartFile pdfFile = request.getFileInput(); MultipartFile pdfFile = request.getFileInput();
String pagesToDelete = request.getPageNumbers(); String pagesToDelete = request.getPageNumbers();
PDDocument document = PDDocument.load(pdfFile.getBytes()); PDDocument document = Loader.loadPDF(pdfFile.getBytes());
// Split the page order string into an array of page numbers or range of numbers // Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pagesToDelete.split(","); String[] pageOrderArr = pagesToDelete.split(",");
@@ -179,7 +180,7 @@ public class RearrangePagesPDFController {
String sortType = request.getCustomMode(); String sortType = request.getCustomMode();
try { try {
// Load the input PDF // Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream()); PDDocument document = Loader.loadPDF(pdfFile.getBytes());
// Split the page order string into an array of page numbers or range of numbers // Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0]; String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];

View File

@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree; import org.apache.pdfbox.pdmodel.PDPageTree;
@@ -37,7 +38,7 @@ public class RotationController {
MultipartFile pdfFile = request.getFileInput(); MultipartFile pdfFile = request.getFileInput();
Integer angle = request.getAngle(); Integer angle = request.getAngle();
// Load the PDF document // Load the PDF document
PDDocument document = PDDocument.load(pdfFile.getBytes()); PDDocument document = Loader.loadPDF(pdfFile.getBytes());
// Get the list of pages in the document // Get the list of pages in the document
PDPageTree pages = document.getPages(); PDPageTree pages = document.getPages();

View File

@@ -5,6 +5,7 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.LayerUtility; import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
@@ -66,7 +67,7 @@ public class ScalePagesController {
PDRectangle targetSize = sizeMap.get(targetPDRectangle); PDRectangle targetSize = sizeMap.get(targetPDRectangle);
PDDocument sourceDocument = PDDocument.load(file.getBytes()); PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
PDDocument outputDocument = new PDDocument(); PDDocument outputDocument = new PDDocument();
int totalPages = sourceDocument.getNumberOfPages(); int totalPages = sourceDocument.getNumberOfPages();
@@ -83,7 +84,11 @@ public class ScalePagesController {
PDPageContentStream contentStream = PDPageContentStream contentStream =
new PDPageContentStream( new PDPageContentStream(
outputDocument, newPage, PDPageContentStream.AppendMode.APPEND, true); outputDocument,
newPage,
PDPageContentStream.AppendMode.APPEND,
true,
true);
float x = (targetSize.getWidth() - sourceSize.getWidth() * scale) / 2; float x = (targetSize.getWidth() - sourceSize.getWidth() * scale) / 2;
float y = (targetSize.getHeight() - sourceSize.getHeight() * scale) / 2; float y = (targetSize.getHeight() - sourceSize.getHeight() * scale) / 2;

View File

@@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.api;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
@@ -11,6 +10,7 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -46,8 +46,8 @@ public class SplitPDFController {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
String pages = request.getPageNumbers(); String pages = request.getPageNumbers();
// open the pdf document // open the pdf document
InputStream inputStream = file.getInputStream();
PDDocument document = PDDocument.load(inputStream); PDDocument document = Loader.loadPDF(file.getBytes());
List<Integer> pageNumbers = request.getPageNumbersList(document); List<Integer> pageNumbers = request.getPageNumbersList(document);
if (!pageNumbers.contains(document.getNumberOfPages() - 1)) if (!pageNumbers.contains(document.getNumberOfPages() - 1))

View File

@@ -9,10 +9,12 @@ import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.LayerUtility; import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
@@ -45,7 +47,7 @@ public class SplitPdfBySectionsController {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
PDDocument sourceDocument = PDDocument.load(file.getInputStream()); PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
// Process the PDF based on split parameters // Process the PDF based on split parameters
int horiz = request.getHorizontalDivisions() + 1; int horiz = request.getHorizontalDivisions() + 1;
@@ -115,13 +117,13 @@ public class SplitPdfBySectionsController {
document, document.getPages().indexOf(originalPage)); document, document.getPages().indexOf(originalPage));
try (PDPageContentStream contentStream = try (PDPageContentStream contentStream =
new PDPageContentStream(subDoc, subPage)) { new PDPageContentStream(
subDoc, subPage, AppendMode.APPEND, true, true)) {
// Set clipping area and position // Set clipping area and position
float translateX = -subPageWidth * i; float translateX = -subPageWidth * i;
float translateY = height - subPageHeight * (verticalDivisions - j);
//float translateY = height - subPageHeight * (verticalDivisions - j);
// Code for google Docs pdfs.. float translateY = -subPageHeight * (verticalDivisions - 1 - j);
// float translateY = -subPageHeight * (verticalDivisions - 1 - j);
contentStream.saveGraphicsState(); contentStream.saveGraphicsState();
contentStream.addRect(0, 0, subPageWidth, subPageHeight); contentStream.addRect(0, 0, subPageWidth, subPageHeight);

View File

@@ -9,6 +9,7 @@ import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@@ -42,7 +43,7 @@ public class SplitPdfBySizeController {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<ByteArrayOutputStream>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<ByteArrayOutputStream>();
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
PDDocument sourceDocument = PDDocument.load(file.getInputStream()); PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
// 0 = size, 1 = page count, 2 = doc count // 0 = size, 1 = page count, 2 = doc count
int type = request.getSplitType(); int type = request.getSplitType();

View File

@@ -4,6 +4,7 @@ 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.Loader;
import org.apache.pdfbox.multipdf.LayerUtility; import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
@@ -40,7 +41,7 @@ public class ToSinglePageController {
throws IOException { throws IOException {
// Load the source document // Load the source document
PDDocument sourceDocument = PDDocument.load(request.getFileInput().getInputStream()); PDDocument sourceDocument = Loader.loadPDF(request.getFileInput().getBytes());
// Calculate total height and max width // Calculate total height and max width
float totalHeight = 0; float totalHeight = 0;

View File

@@ -13,6 +13,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -20,13 +21,18 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView; import org.springframework.web.servlet.view.RedirectView;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.config.security.UserService; import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
import stirling.software.SPDF.model.api.user.UpdateUserDetails;
import stirling.software.SPDF.model.api.user.UsernameAndPass;
@Controller @Controller
@Tag(name = "User", description = "User APIs")
@RequestMapping("/api/v1/user") @RequestMapping("/api/v1/user")
public class UserController { public class UserController {
@@ -34,14 +40,13 @@ public class UserController {
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@PostMapping("/register") @PostMapping("/register")
public String register( public String register(@ModelAttribute UsernameAndPass requestModel, Model model) {
@RequestParam String username, @RequestParam String password, Model model) { if (userService.usernameExists(requestModel.getUsername())) {
if (userService.usernameExists(username)) {
model.addAttribute("error", "Username already exists"); model.addAttribute("error", "Username already exists");
return "register"; return "register";
} }
userService.saveUser(username, password); userService.saveUser(requestModel.getUsername(), requestModel.getPassword());
return "redirect:/login?registered=true"; return "redirect:/login?registered=true";
} }
@@ -49,12 +54,15 @@ public class UserController {
@PostMapping("/change-username-and-password") @PostMapping("/change-username-and-password")
public RedirectView changeUsernameAndPassword( public RedirectView changeUsernameAndPassword(
Principal principal, Principal principal,
@RequestParam String currentPassword, @ModelAttribute UpdateUserDetails requestModel,
@RequestParam String newUsername,
@RequestParam String newPassword,
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
RedirectAttributes redirectAttributes) { RedirectAttributes redirectAttributes) {
String currentPassword = requestModel.getPassword();
String newPassword = requestModel.getNewPassword();
String newUsername = requestModel.getNewUsername();
if (principal == null) { if (principal == null) {
return new RedirectView("/change-creds?messageType=notAuthenticated"); return new RedirectView("/change-creds?messageType=notAuthenticated");
} }

View File

@@ -0,0 +1,68 @@
package stirling.software.SPDF.controller.api.converters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.GeneralFile;
import stirling.software.SPDF.utils.FileToPdf;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@Tag(name = "Convert", description = "Convert APIs")
@RequestMapping("/api/v1/convert")
public class ConvertBookToPDFController {
@Autowired
@Qualifier("bookFormatsInstalled")
private boolean bookFormatsInstalled;
@PostMapping(consumes = "multipart/form-data", value = "/book/pdf")
@Operation(
summary =
"Convert a BOOK/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx) to PDF",
description =
"(Requires bookFormatsInstalled flag and Calibre installed) This endpoint takes an BOOK/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx) input and converts it to PDF format.")
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute GeneralFile request) throws Exception {
MultipartFile fileInput = request.getFileInput();
if (!bookFormatsInstalled) {
throw new IllegalArgumentException(
"bookFormatsInstalled flag is False, this functionality is not avaiable");
}
if (fileInput == null) {
throw new IllegalArgumentException("Please provide a file for conversion.");
}
String originalFilename = fileInput.getOriginalFilename();
if (originalFilename != null) {
String originalFilenameLower = originalFilename.toLowerCase();
if (!originalFilenameLower.endsWith(".epub")
&& !originalFilenameLower.endsWith(".mobi")
&& !originalFilenameLower.endsWith(".azw3")
&& !originalFilenameLower.endsWith(".fb2")
&& !originalFilenameLower.endsWith(".txt")
&& !originalFilenameLower.endsWith(".docx")) {
throw new IllegalArgumentException(
"File must be in .epub, .mobi, .azw3, .fb2, .txt, or .docx format.");
}
}
byte[] pdfBytes = FileToPdf.convertBookTypeToPdf(fileInput.getBytes(), originalFilename);
String outputFilename =
originalFilename.replaceFirst("[.][^.]+$", "")
+ ".pdf"; // Remove file extension and append .pdf
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
}

View File

@@ -1,5 +1,7 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -19,6 +21,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
public class ConvertHtmlToPDF { public class ConvertHtmlToPDF {
@Autowired
@Qualifier("htmlFormatsInstalled")
private boolean htmlFormatsInstalled;
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf") @PostMapping(consumes = "multipart/form-data", value = "/html/pdf")
@Operation( @Operation(
summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF", summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF",
@@ -37,7 +43,9 @@ public class ConvertHtmlToPDF {
|| (!originalFilename.endsWith(".html") && !originalFilename.endsWith(".zip"))) { || (!originalFilename.endsWith(".html") && !originalFilename.endsWith(".zip"))) {
throw new IllegalArgumentException("File must be either .html or .zip format."); throw new IllegalArgumentException("File must be either .html or .zip format.");
} }
byte[] pdfBytes = FileToPdf.convertHtmlToPdf(fileInput.getBytes(), originalFilename); byte[] pdfBytes =
FileToPdf.convertHtmlToPdf(
fileInput.getBytes(), originalFilename, htmlFormatsInstalled);
String outputFilename = String outputFilename =
originalFilename.replaceFirst("[.][^.]+$", "") originalFilename.replaceFirst("[.][^.]+$", "")

View File

@@ -1,8 +1,17 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
import java.util.List;
import java.util.Map;
import org.commonmark.Extension;
import org.commonmark.ext.gfm.tables.TableBlock;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.node.Node; import org.commonmark.node.Node;
import org.commonmark.parser.Parser; import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -22,6 +31,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
public class ConvertMarkdownToPdf { public class ConvertMarkdownToPdf {
@Autowired
@Qualifier("htmlFormatsInstalled")
private boolean htmlFormatsInstalled;
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf") @PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf")
@Operation( @Operation(
summary = "Convert a Markdown file to PDF", summary = "Convert a Markdown file to PDF",
@@ -41,12 +54,21 @@ public class ConvertMarkdownToPdf {
} }
// Convert Markdown to HTML using CommonMark // Convert Markdown to HTML using CommonMark
Parser parser = Parser.builder().build(); List<Extension> extensions = List.of(TablesExtension.create());
Parser parser = Parser.builder().extensions(extensions).build();
Node document = parser.parse(new String(fileInput.getBytes())); Node document = parser.parse(new String(fileInput.getBytes()));
HtmlRenderer renderer = HtmlRenderer.builder().build(); HtmlRenderer renderer =
HtmlRenderer.builder()
.attributeProviderFactory(context -> new TableAttributeProvider())
.extensions(extensions)
.build();
String htmlContent = renderer.render(document); String htmlContent = renderer.render(document);
byte[] pdfBytes = FileToPdf.convertHtmlToPdf(htmlContent.getBytes(), "converted.html"); byte[] pdfBytes =
FileToPdf.convertHtmlToPdf(
htmlContent.getBytes(), "converted.html", htmlFormatsInstalled);
String outputFilename = String outputFilename =
originalFilename.replaceFirst("[.][^.]+$", "") originalFilename.replaceFirst("[.][^.]+$", "")
@@ -54,3 +76,12 @@ public class ConvertMarkdownToPdf {
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename); return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
} }
} }
class TableAttributeProvider implements AttributeProvider {
@Override
public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
if (node instanceof TableBlock) {
attributes.put("class", "table table-striped");
}
}
}

View File

@@ -0,0 +1,101 @@
package stirling.software.SPDF.controller.api.converters;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.converters.PdfToBookRequest;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@Tag(name = "Convert", description = "Convert APIs")
@RequestMapping("/api/v1/convert")
public class ConvertPDFToBookController {
@Autowired
@Qualifier("bookFormatsInstalled")
private boolean bookFormatsInstalled;
@PostMapping(consumes = "multipart/form-data", value = "/pdf/book")
@Operation(
summary =
"Convert a PDF to a Book/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx .. (others to include by chatgpt) to PDF",
description =
"(Requires bookFormatsInstalled flag and Calibre installed) This endpoint Convert a PDF to a Book/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx .. (others to include by chatgpt) to PDF")
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute PdfToBookRequest request)
throws Exception {
MultipartFile fileInput = request.getFileInput();
if (!bookFormatsInstalled) {
throw new IllegalArgumentException(
"bookFormatsInstalled flag is False, this functionality is not avaiable");
}
if (fileInput == null) {
throw new IllegalArgumentException("Please provide a file for conversion.");
}
// Validate the output format
String outputFormat = request.getOutputFormat().toLowerCase();
List<String> allowedFormats =
Arrays.asList(
"epub", "mobi", "azw3", "docx", "rtf", "txt", "html", "lit", "fb2", "pdb",
"lrf");
if (!allowedFormats.contains(outputFormat)) {
throw new IllegalArgumentException("Invalid output format: " + outputFormat);
}
byte[] outputFileBytes;
List<String> command = new ArrayList<>();
Path tempOutputFile =
Files.createTempFile(
"output_",
"." + outputFormat); // Use the output format for the file extension
Path tempInputFile = null;
try {
// Create temp input file from the provided PDF
tempInputFile = Files.createTempFile("input_", ".pdf"); // Assuming input is always PDF
Files.write(tempInputFile, fileInput.getBytes());
command.add("ebook-convert");
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.CALIBRE)
.runCommandWithOutputHandling(command);
outputFileBytes = Files.readAllBytes(tempOutputFile);
} finally {
// Clean up temporary files
if (tempInputFile != null) {
Files.deleteIfExists(tempInputFile);
}
Files.deleteIfExists(tempOutputFile);
}
String outputFilename =
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")
+ "."
+ outputFormat; // Remove file extension and append .pdf
return WebResponseUtils.bytesToWebResponse(outputFileBytes, outputFilename);
}
}

View File

@@ -6,6 +6,8 @@ import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -26,6 +28,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
public class ConvertWebsiteToPDF { public class ConvertWebsiteToPDF {
@Autowired
@Qualifier("htmlFormatsInstalled")
private boolean htmlFormatsInstalled;
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf") @PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
@Operation( @Operation(
summary = "Convert a URL to a PDF", summary = "Convert a URL to a PDF",
@@ -47,7 +53,11 @@ public class ConvertWebsiteToPDF {
// Prepare the OCRmyPDF command // Prepare the OCRmyPDF command
List<String> command = new ArrayList<>(); List<String> command = new ArrayList<>();
command.add("weasyprint"); if (!htmlFormatsInstalled) {
command.add("weasyprint");
} else {
command.add("wkhtmltopdf");
}
command.add(URL); command.add(URL);
command.add(tempOutputFile.toString()); command.add(tempOutputFile.toString());

View File

@@ -1,10 +1,10 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
import java.io.ByteArrayInputStream;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -44,8 +44,7 @@ public class ExtractController {
ArrayList<String> tableData = new ArrayList<>(); ArrayList<String> tableData = new ArrayList<>();
int columnsCount = 0; int columnsCount = 0;
try (PDDocument document = try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
PDDocument.load(new ByteArrayInputStream(form.getFileInput().getBytes()))) {
final double res = 72; // PDF units are at 72 DPI final double res = 72; // PDF units are at 72 DPI
PDFTableStripper stripper = new PDFTableStripper(); PDFTableStripper stripper = new PDFTableStripper();
PDPage pdPage = document.getPage(form.getPageId() - 1); PDPage pdPage = document.getPage(form.getPageId() - 1);

View File

@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api.filters;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
@@ -39,7 +40,7 @@ public class FilterController {
String text = request.getText(); String text = request.getText();
String pageNumber = request.getPageNumbers(); String pageNumber = request.getPageNumbers();
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream()); PDDocument pdfDocument = Loader.loadPDF(inputFile.getBytes());
if (PdfUtils.hasText(pdfDocument, pageNumber, text)) if (PdfUtils.hasText(pdfDocument, pageNumber, text))
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
pdfDocument, inputFile.getOriginalFilename()); pdfDocument, inputFile.getOriginalFilename());
@@ -56,7 +57,7 @@ public class FilterController {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
String pageNumber = request.getPageNumbers(); String pageNumber = request.getPageNumbers();
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream()); PDDocument pdfDocument = Loader.loadPDF(inputFile.getBytes());
if (PdfUtils.hasImages(pdfDocument, pageNumber)) if (PdfUtils.hasImages(pdfDocument, pageNumber))
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
pdfDocument, inputFile.getOriginalFilename()); pdfDocument, inputFile.getOriginalFilename());
@@ -73,7 +74,7 @@ public class FilterController {
String pageCount = request.getPageCount(); String pageCount = request.getPageCount();
String comparator = request.getComparator(); String comparator = request.getComparator();
// Load the PDF // Load the PDF
PDDocument document = PDDocument.load(inputFile.getInputStream()); PDDocument document = Loader.loadPDF(inputFile.getBytes());
int actualPageCount = document.getNumberOfPages(); int actualPageCount = document.getNumberOfPages();
boolean valid = false; boolean valid = false;
@@ -107,7 +108,7 @@ public class FilterController {
String comparator = request.getComparator(); String comparator = request.getComparator();
// Load the PDF // Load the PDF
PDDocument document = PDDocument.load(inputFile.getInputStream()); PDDocument document = Loader.loadPDF(inputFile.getBytes());
PDPage firstPage = document.getPage(0); PDPage firstPage = document.getPage(0);
PDRectangle actualPageSize = firstPage.getMediaBox(); PDRectangle actualPageSize = firstPage.getMediaBox();
@@ -183,7 +184,7 @@ public class FilterController {
String comparator = request.getComparator(); String comparator = request.getComparator();
// Load the PDF // Load the PDF
PDDocument document = PDDocument.load(inputFile.getInputStream()); PDDocument document = Loader.loadPDF(inputFile.getBytes());
// Get the rotation of the first page // Get the rotation of the first page
PDPage firstPage = document.getPage(0); PDPage firstPage = document.getPage(0);

View File

@@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition; import org.apache.pdfbox.text.TextPosition;
@@ -43,7 +44,7 @@ public class AutoRenameController {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback(); Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback();
PDDocument document = PDDocument.load(file.getInputStream()); PDDocument document = Loader.loadPDF(file.getBytes());
PDFTextStripper reader = PDFTextStripper reader =
new PDFTextStripper() { new PDFTextStripper() {
class LineInfo { class LineInfo {

View File

@@ -5,7 +5,6 @@ import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt; import java.awt.image.DataBufferInt;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
@@ -13,6 +12,7 @@ import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@@ -54,8 +54,7 @@ public class AutoSplitPdfController {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
boolean duplexMode = request.isDuplexMode(); boolean duplexMode = request.isDuplexMode();
InputStream inputStream = file.getInputStream(); PDDocument document = Loader.loadPDF(file.getBytes());
PDDocument document = PDDocument.load(inputStream);
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);
List<PDDocument> splitDocuments = new ArrayList<>(); List<PDDocument> splitDocuments = new ArrayList<>();

View File

@@ -13,6 +13,7 @@ import java.util.stream.IntStream;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree; import org.apache.pdfbox.pdmodel.PDPageTree;
@@ -53,7 +54,7 @@ public class BlankPageController {
PDDocument document = null; PDDocument document = null;
try { try {
document = PDDocument.load(inputFile.getInputStream()); document = Loader.loadPDF(inputFile.getBytes());
PDPageTree pages = document.getDocumentCatalog().getPages(); PDPageTree pages = document.getDocumentCatalog().getPages();
PDFTextStripper textStripper = new PDFTextStripper(); PDFTextStripper textStripper = new PDFTextStripper();

View File

@@ -13,6 +13,7 @@ import java.util.List;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
@@ -147,7 +148,7 @@ public class CompressController {
if (expectedOutputSize != null && autoMode) { if (expectedOutputSize != null && autoMode) {
long outputFileSize = Files.size(tempOutputFile); long outputFileSize = Files.size(tempOutputFile);
if (outputFileSize > expectedOutputSize) { if (outputFileSize > expectedOutputSize) {
try (PDDocument doc = PDDocument.load(new File(tempOutputFile.toString()))) { try (PDDocument doc = Loader.loadPDF(new File(tempOutputFile.toString()))) {
long previousFileSize = 0; long previousFileSize = 0;
double scaleFactor = 1.0; double scaleFactor = 1.0;
while (true) { while (true) {

View File

@@ -1,7 +1,6 @@
package stirling.software.SPDF.controller.api.misc; package stirling.software.SPDF.controller.api.misc;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@@ -17,6 +16,7 @@ import java.util.zip.ZipOutputStream;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -76,8 +76,7 @@ public class ExtractImageScansController {
// Check if input file is a PDF // Check if input file is a PDF
if (extension.equalsIgnoreCase("pdf")) { if (extension.equalsIgnoreCase("pdf")) {
// Load PDF document // Load PDF document
try (PDDocument document = try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
PDDocument.load(new ByteArrayInputStream(form.getFileInput().getBytes()))) {
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);
int pageCount = document.getNumberOfPages(); int pageCount = document.getNumberOfPages();
images = new ArrayList<>(); images = new ArrayList<>();

View File

@@ -14,6 +14,7 @@ import java.util.zip.ZipOutputStream;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
@@ -53,7 +54,7 @@ public class ExtractImagesController {
System.out.println( System.out.println(
System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format); System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format);
PDDocument document = PDDocument.load(file.getBytes()); PDDocument document = Loader.loadPDF(file.getBytes());
// Create ByteArrayOutputStream to write zip file to byte array // Create ByteArrayOutputStream to write zip file to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();

View File

@@ -16,6 +16,7 @@ import java.util.Random;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
@@ -57,7 +58,7 @@ public class FakeScanControllerWIP {
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request) throws IOException { public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request) throws IOException {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
PDDocument document = PDDocument.load(inputFile.getBytes()); PDDocument document = Loader.loadPDF(inputFile.getBytes());
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); ++page) { for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB); BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);

View File

@@ -7,6 +7,7 @@ import java.util.Calendar;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.apache.pdfbox.pdmodel.PDDocumentInformation;
@@ -67,7 +68,7 @@ public class MetadataController {
allRequestParams = new java.util.HashMap<String, String>(); allRequestParams = new java.util.HashMap<String, String>();
} }
// Load the PDF file into a PDDocument // Load the PDF file into a PDDocument
PDDocument document = PDDocument.load(pdfFile.getBytes()); PDDocument document = Loader.loadPDF(pdfFile.getBytes());
// Get the document information from the PDF // Get the document information from the PDF
PDDocumentInformation info = document.getDocumentInformation(); PDDocumentInformation info = document.getDocumentInformation();

View File

@@ -4,11 +4,13 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
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;
@@ -48,7 +50,7 @@ public class PageNumbersController {
String customText = request.getCustomText(); String customText = request.getCustomText();
int pageNumber = startingNumber; int pageNumber = startingNumber;
byte[] fileBytes = file.getBytes(); byte[] fileBytes = file.getBytes();
PDDocument document = PDDocument.load(fileBytes); PDDocument document = Loader.loadPDF(fileBytes);
float marginFactor; float marginFactor;
switch (customMargin.toLowerCase()) { switch (customMargin.toLowerCase()) {
@@ -71,7 +73,6 @@ public class PageNumbersController {
} }
float fontSize = 12.0f; float fontSize = 12.0f;
PDType1Font font = PDType1Font.HELVETICA;
if (pagesToNumber == null || pagesToNumber.length() == 0) { if (pagesToNumber == null || pagesToNumber.length() == 0) {
pagesToNumber = "all"; pagesToNumber = "all";
} }
@@ -127,9 +128,9 @@ public class PageNumbersController {
PDPageContentStream contentStream = PDPageContentStream contentStream =
new PDPageContentStream( new PDPageContentStream(
document, page, PDPageContentStream.AppendMode.APPEND, true); document, page, PDPageContentStream.AppendMode.APPEND, true, true);
contentStream.beginText(); contentStream.beginText();
contentStream.setFont(font, fontSize); contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), fontSize);
contentStream.newLineAtOffset(x, y); contentStream.newLineAtOffset(x, y);
contentStream.showText(text); contentStream.showText(text);
contentStream.endText(); contentStream.endText();

View File

@@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api.misc;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Map; import java.util.Map;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode; import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript; import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
@@ -36,7 +37,7 @@ public class ShowJavascript {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
String script = ""; String script = "";
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) { try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
if (document.getDocumentCatalog() != null if (document.getDocumentCatalog() != null
&& document.getDocumentCatalog().getNames() != null) { && document.getDocumentCatalog().getNames() != null) {

View File

@@ -16,6 +16,7 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.util.Calendar; import java.util.Calendar;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.examples.signature.CreateSignatureBase; import org.apache.pdfbox.examples.signature.CreateSignatureBase;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
@@ -132,7 +133,7 @@ public class CertSignController {
String name, String name,
String location, String location,
String reason) { String reason) {
try (PDDocument doc = PDDocument.load(input)) { try (PDDocument doc = Loader.loadPDF(input)) {
PDSignature signature = new PDSignature(); PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

View File

@@ -11,11 +11,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.pdfbox.cos.COSDocument; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSInputStream; import org.apache.pdfbox.cos.COSInputStream;
import org.apache.pdfbox.cos.COSName; 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.PDDocumentCatalog;
@@ -87,7 +85,7 @@ public class GetInfoOnPDF {
@Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO") @Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO")
public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException { public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
try (PDDocument pdfBoxDoc = PDDocument.load(inputFile.getInputStream()); ) { try (PDDocument pdfBoxDoc = Loader.loadPDF(inputFile.getBytes()); ) {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
ObjectNode jsonOutput = objectMapper.createObjectNode(); ObjectNode jsonOutput = objectMapper.createObjectNode();
@@ -129,17 +127,6 @@ public class GetInfoOnPDF {
boolean hasCompression = false; boolean hasCompression = false;
String compressionType = "None"; String compressionType = "None";
COSDocument cosDoc = pdfBoxDoc.getDocument();
for (COSObject cosObject : cosDoc.getObjects()) {
if (cosObject.getObject() instanceof COSStream) {
COSStream cosStream = (COSStream) cosObject.getObject();
if (COSName.OBJ_STM.equals(cosStream.getItem(COSName.TYPE))) {
hasCompression = true;
compressionType = "Object Streams";
break;
}
}
}
basicInfo.put("Compression", hasCompression); basicInfo.put("Compression", hasCompression);
if (hasCompression) basicInfo.put("CompressionType", compressionType); if (hasCompression) basicInfo.put("CompressionType", compressionType);
@@ -343,7 +330,6 @@ public class GetInfoOnPDF {
permissionsNode.put("CanModify", ap.canModify()); permissionsNode.put("CanModify", ap.canModify());
permissionsNode.put("CanModifyAnnotations", ap.canModifyAnnotations()); permissionsNode.put("CanModifyAnnotations", ap.canModifyAnnotations());
permissionsNode.put("CanPrint", ap.canPrint()); permissionsNode.put("CanPrint", ap.canPrint());
permissionsNode.put("CanPrintDegraded", ap.canPrintDegraded());
encryption.set( encryption.set(
"Permissions", permissionsNode); // set the node under "Permissions" "Permissions", permissionsNode); // set the node under "Permissions"

View File

@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api.security;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission; import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
@@ -38,7 +39,7 @@ public class PasswordController {
MultipartFile fileInput = request.getFileInput(); MultipartFile fileInput = request.getFileInput();
String password = request.getPassword(); String password = request.getPassword();
PDDocument document = PDDocument.load(fileInput.getBytes(), password); PDDocument document = Loader.loadPDF(fileInput.getBytes(), password);
document.setAllSecurityToBeRemoved(true); document.setAllSecurityToBeRemoved(true);
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
document, document,
@@ -66,7 +67,7 @@ public class PasswordController {
boolean canPrint = request.isCanPrint(); boolean canPrint = request.isCanPrint();
boolean canPrintFaithful = request.isCanPrintFaithful(); boolean canPrintFaithful = request.isCanPrintFaithful();
PDDocument document = PDDocument.load(fileInput.getBytes()); PDDocument document = Loader.loadPDF(fileInput.getBytes());
AccessPermission ap = new AccessPermission(); AccessPermission ap = new AccessPermission();
ap.setCanAssembleDocument(!canAssembleDocument); ap.setCanAssembleDocument(!canAssembleDocument);
ap.setCanExtractContent(!canExtractContent); ap.setCanExtractContent(!canExtractContent);

View File

@@ -2,14 +2,15 @@ package stirling.software.SPDF.controller.api.security;
import java.awt.Color; import java.awt.Color;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
@@ -57,7 +58,7 @@ public class RedactController {
System.out.println(listOfTextString); System.out.println(listOfTextString);
String[] listOfText = listOfTextString.split("\n"); String[] listOfText = listOfTextString.split("\n");
byte[] bytes = file.getBytes(); byte[] bytes = file.getBytes();
PDDocument document = PDDocument.load(new ByteArrayInputStream(bytes)); PDDocument document = Loader.loadPDF(bytes);
Color redactColor; Color redactColor;
try { try {
@@ -86,7 +87,9 @@ public class RedactController {
PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight())); PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight()));
imageDocument.addPage(newPage); imageDocument.addPage(newPage);
PDImageXObject pdImage = LosslessFactory.createFromImage(imageDocument, bim); PDImageXObject pdImage = LosslessFactory.createFromImage(imageDocument, bim);
PDPageContentStream contentStream = new PDPageContentStream(imageDocument, newPage); PDPageContentStream contentStream =
new PDPageContentStream(
imageDocument, newPage, AppendMode.APPEND, true, true);
contentStream.drawImage(pdImage, 0, 0); contentStream.drawImage(pdImage, 0, 0);
contentStream.close(); contentStream.close();
} }

View File

@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api.security;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
@@ -52,7 +53,7 @@ public class SanitizeController {
boolean removeLinks = request.isRemoveLinks(); boolean removeLinks = request.isRemoveLinks();
boolean removeFonts = request.isRemoveFonts(); boolean removeFonts = request.isRemoveFonts();
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) { try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
if (removeJavaScript) { if (removeJavaScript) {
sanitizeJavaScript(document); sanitizeJavaScript(document);
} }

View File

@@ -10,12 +10,14 @@ import java.io.InputStream;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.font.PDType1Font; 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.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
@@ -58,7 +60,7 @@ public class WatermarkController {
int heightSpacer = request.getHeightSpacer(); int heightSpacer = request.getHeightSpacer();
// Load the input PDF // Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream()); PDDocument document = Loader.loadPDF(pdfFile.getBytes());
// Create a page in the document // Create a page in the document
for (PDPage page : document.getPages()) { for (PDPage page : document.getPages()) {
@@ -66,7 +68,7 @@ public class WatermarkController {
// Get the page's content stream // Get the page's content stream
PDPageContentStream contentStream = PDPageContentStream contentStream =
new PDPageContentStream( new PDPageContentStream(
document, page, PDPageContentStream.AppendMode.APPEND, true); document, page, PDPageContentStream.AppendMode.APPEND, true, true);
// Set transparency // Set transparency
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState(); PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
@@ -117,7 +119,7 @@ public class WatermarkController {
String alphabet) String alphabet)
throws IOException { throws IOException {
String resourceDir = ""; String resourceDir = "";
PDFont font = PDType1Font.HELVETICA_BOLD; PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
switch (alphabet) { switch (alphabet) {
case "arabic": case "arabic":
resourceDir = "static/fonts/NotoSansArabic-Regular.ttf"; resourceDir = "static/fonts/NotoSansArabic-Regular.ttf";

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.controller.web; package stirling.software.SPDF.controller.web;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -12,6 +13,22 @@ import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name = "Convert", description = "Convert APIs") @Tag(name = "Convert", description = "Convert APIs")
public class ConverterWebController { public class ConverterWebController {
@ConditionalOnExpression("#{bookFormatsInstalled}")
@GetMapping("/book-to-pdf")
@Hidden
public String convertBookToPdfForm(Model model) {
model.addAttribute("currentPage", "book-to-pdf");
return "convert/book-to-pdf";
}
@ConditionalOnExpression("#{bookFormatsInstalled}")
@GetMapping("/pdf-to-book")
@Hidden
public String convertPdfToBookForm(Model model) {
model.addAttribute("currentPage", "pdf-to-book");
return "convert/pdf-to-book";
}
@GetMapping("/img-to-pdf") @GetMapping("/img-to-pdf")
@Hidden @Hidden
public String convertImgToPdfForm(Model model) { public String convertImgToPdfForm(Model model) {

View File

@@ -1,7 +1,8 @@
package stirling.software.SPDF.controller.web; package stirling.software.SPDF.controller.web;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -38,7 +39,8 @@ public class HomeWebController {
model.addAttribute("currentPage", "licenses"); model.addAttribute("currentPage", "licenses");
Resource resource = new ClassPathResource("static/3rdPartyLicenses.json"); Resource resource = new ClassPathResource("static/3rdPartyLicenses.json");
try { try {
String json = new String(Files.readAllBytes(resource.getFile().toPath())); InputStream is = resource.getInputStream();
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
Map<String, List<Dependency>> data = Map<String, List<Dependency>> data =
mapper.readValue(json, new TypeReference<Map<String, List<Dependency>>>() {}); mapper.readValue(json, new TypeReference<Map<String, List<Dependency>>>() {});

View File

@@ -210,6 +210,7 @@ public class ApplicationProperties {
private String rootURIPath; private String rootURIPath;
private String customStaticFilePath; private String customStaticFilePath;
private Integer maxFileSize; private Integer maxFileSize;
private CustomApplications customApplications;
private Boolean enableAlphaFunctionality; private Boolean enableAlphaFunctionality;
@@ -261,6 +262,14 @@ public class ApplicationProperties {
this.maxFileSize = maxFileSize; this.maxFileSize = maxFileSize;
} }
public CustomApplications getCustomApplications() {
return customApplications != null ? customApplications : new CustomApplications();
}
public void setCustomApplications(CustomApplications customApplications) {
this.customApplications = customApplications;
}
@Override @Override
public String toString() { public String toString() {
return "System [defaultLocale=" return "System [defaultLocale="
@@ -273,10 +282,42 @@ public class ApplicationProperties {
+ customStaticFilePath + customStaticFilePath
+ ", maxFileSize=" + ", maxFileSize="
+ maxFileSize + maxFileSize
+ ", customApplications="
+ customApplications
+ ", enableAlphaFunctionality=" + ", enableAlphaFunctionality="
+ enableAlphaFunctionality + enableAlphaFunctionality
+ "]"; + "]";
} }
public static class CustomApplications {
private boolean installBookFormats;
private boolean installAdvancedHtmlToPDF;
public boolean isInstallBookFormats() {
return installBookFormats;
}
public void setInstallBookFormats(boolean installBookFormats) {
this.installBookFormats = installBookFormats;
}
public boolean isInstallAdvancedHtmlToPDF() {
return installAdvancedHtmlToPDF;
}
public void setInstallAdvancedHtmlToPDF(boolean installAdvancedHtmlToPDF) {
this.installAdvancedHtmlToPDF = installAdvancedHtmlToPDF;
}
@Override
public String toString() {
return "CustomApplications [installBookFormats="
+ installBookFormats
+ ", installAdvancedHtmlToPDF="
+ installAdvancedHtmlToPDF
+ "]";
}
}
} }
public static class Ui { public static class Ui {

View File

@@ -3,6 +3,7 @@ package stirling.software.SPDF.model.api;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
@@ -27,7 +28,7 @@ public class PDFWithPageNums extends PDFFile {
public List<Integer> getPageNumbersList() { public List<Integer> getPageNumbersList() {
int pageCount = 0; int pageCount = 0;
try { try {
pageCount = PDDocument.load(getFileInput().getInputStream()).getNumberOfPages(); pageCount = Loader.loadPDF(getFileInput().getBytes()).getNumberOfPages();
} catch (IOException e) { } catch (IOException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();

View File

@@ -0,0 +1,19 @@
package stirling.software.SPDF.model.api.converters;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class PdfToBookRequest extends PDFFile {
@Schema(
description = "The output Ebook format",
allowableValues = {
"epub", "mobi", "azw3", "docx", "rtf", "txt", "html", "lit", "fb2", "pdb", "lrf"
})
private String outputFormat;
}

View File

@@ -0,0 +1,16 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class UpdateUserDetails extends UpdateUserUsername {
@Schema(description = "new password for user")
private String newPassword;
}

View File

@@ -0,0 +1,16 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class UpdateUserUsername extends UsernameAndPass {
@Schema(description = "new password for user")
private String newUsername;
}

View File

@@ -0,0 +1,16 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@EqualsAndHashCode
@NoArgsConstructor
public class Username {
@Schema(description = "username of user")
private String username;
}

View File

@@ -0,0 +1,16 @@
package stirling.software.SPDF.model.api.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class UsernameAndPass extends Username {
@Schema(description = "password of user")
private String password;
}

View File

@@ -14,7 +14,9 @@ import java.util.zip.ZipInputStream;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
public class FileToPdf { public class FileToPdf {
public static byte[] convertHtmlToPdf(byte[] fileBytes, String fileName)
public static byte[] convertHtmlToPdf(
byte[] fileBytes, String fileName, boolean htmlFormatsInstalled)
throws IOException, InterruptedException { throws IOException, InterruptedException {
Path tempOutputFile = Files.createTempFile("output_", ".pdf"); Path tempOutputFile = Files.createTempFile("output_", ".pdf");
@@ -29,11 +31,22 @@ public class FileToPdf {
} }
List<String> command = new ArrayList<>(); List<String> command = new ArrayList<>();
command.add("weasyprint"); if (!htmlFormatsInstalled) {
command.add("weasyprint");
} else {
command.add("wkhtmltopdf");
command.add("--enable-local-file-access");
}
command.add(tempInputFile.toString()); command.add(tempInputFile.toString());
command.add(tempOutputFile.toString()); command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode; ProcessExecutorResult returnCode;
if (fileName.endsWith(".zip")) { if (fileName.endsWith(".zip")) {
if (htmlFormatsInstalled) {
// command.add(1, "--allow");
// command.add(2, tempInputFile.getParent().toString());
}
returnCode = returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT) ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
.runCommandWithOutputHandling( .runCommandWithOutputHandling(
@@ -97,4 +110,38 @@ public class FileToPdf {
return htmlFiles.get(0); return htmlFiles.get(0);
} }
} }
public static byte[] convertBookTypeToPdf(byte[] bytes, String originalFilename)
throws IOException, InterruptedException {
if (originalFilename == null || originalFilename.lastIndexOf('.') == -1) {
throw new IllegalArgumentException("Invalid original filename.");
}
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf('.'));
List<String> command = new ArrayList<>();
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
Path tempInputFile = null;
try {
// Create temp file with appropriate extension
tempInputFile = Files.createTempFile("input_", fileExtension);
Files.write(tempInputFile, bytes);
command.add("ebook-convert");
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.CALIBRE)
.runCommandWithOutputHandling(command);
return Files.readAllBytes(tempOutputFile);
} finally {
// Clean up temporary files
if (tempInputFile != null) {
Files.deleteIfExists(tempInputFile);
}
Files.deleteIfExists(tempOutputFile);
}
}
} }

View File

@@ -19,6 +19,14 @@ import org.springframework.web.multipart.MultipartFile;
public class GeneralUtils { public class GeneralUtils {
public static File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException {
File tempFile = File.createTempFile("temp", null);
try (FileOutputStream os = new FileOutputStream(tempFile)) {
os.write(multipartFile.getBytes());
}
return tempFile;
}
public static void deleteDirectory(Path path) throws IOException { public static void deleteDirectory(Path path) throws IOException {
Files.walkFileTree( Files.walkFileTree(
path, path,

View File

@@ -2,7 +2,6 @@ package stirling.software.SPDF.utils;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@@ -16,9 +15,11 @@ import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory; import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
@@ -190,7 +191,7 @@ public class PdfUtils {
int DPI, int DPI,
String filename) String filename)
throws IOException, Exception { throws IOException, Exception {
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) { try (PDDocument document = Loader.loadPDF(inputStream)) {
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);
int pageCount = document.getNumberOfPages(); int pageCount = document.getNumberOfPages();
@@ -335,7 +336,8 @@ public class PdfUtils {
float pageWidth = page.getMediaBox().getWidth(); float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight(); float pageHeight = page.getMediaBox().getHeight();
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) { try (PDPageContentStream contentStream =
new PDPageContentStream(doc, page, AppendMode.APPEND, true, true)) {
if ("fillPage".equals(fitOption) || "fitDocumentToImage".equals(fitOption)) { if ("fillPage".equals(fitOption) || "fitDocumentToImage".equals(fitOption)) {
contentStream.drawImage(image, 0, 0, pageWidth, pageHeight); contentStream.drawImage(image, 0, 0, pageWidth, pageHeight);
} else if ("maintainAspectRatio".equals(fitOption)) { } else if ("maintainAspectRatio".equals(fitOption)) {
@@ -368,7 +370,7 @@ public class PdfUtils {
byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage)
throws IOException { throws IOException {
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes)); PDDocument document = Loader.loadPDF(pdfBytes);
// Get the first page of the PDF // Get the first page of the PDF
int pages = document.getNumberOfPages(); int pages = document.getNumberOfPages();
@@ -376,7 +378,7 @@ public class PdfUtils {
PDPage page = document.getPage(i); PDPage page = document.getPage(i);
try (PDPageContentStream contentStream = try (PDPageContentStream contentStream =
new PDPageContentStream( new PDPageContentStream(
document, page, PDPageContentStream.AppendMode.APPEND, true)) { document, page, PDPageContentStream.AppendMode.APPEND, true, true)) {
// Create an image object from the image bytes // Create an image object from the image bytes
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, ""); PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
// Draw the image onto the page at the specified x and y coordinates // Draw the image onto the page at the specified x and y coordinates

View File

@@ -4,26 +4,39 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProcessExecutor { public class ProcessExecutor {
private static final Logger logger = LoggerFactory.getLogger(ProcessExecutor.class);
public enum Processes { public enum Processes {
LIBRE_OFFICE, LIBRE_OFFICE,
OCR_MY_PDF, OCR_MY_PDF,
PYTHON_OPENCV, PYTHON_OPENCV,
GHOSTSCRIPT, GHOSTSCRIPT,
WEASYPRINT WEASYPRINT,
INSTALL_APP,
CALIBRE
} }
private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>(); private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>();
public static ProcessExecutor getInstance(Processes processType) { public static ProcessExecutor getInstance(Processes processType) {
return getInstance(processType, true);
}
public static ProcessExecutor getInstance(Processes processType, boolean liveUpdates) {
return instances.computeIfAbsent( return instances.computeIfAbsent(
processType, processType,
key -> { key -> {
@@ -34,15 +47,32 @@ public class ProcessExecutor {
case PYTHON_OPENCV -> 8; case PYTHON_OPENCV -> 8;
case GHOSTSCRIPT -> 16; case GHOSTSCRIPT -> 16;
case WEASYPRINT -> 16; case WEASYPRINT -> 16;
case INSTALL_APP -> 1;
case CALIBRE -> 1;
}; };
return new ProcessExecutor(semaphoreLimit);
long timeoutMinutes =
switch (key) {
case LIBRE_OFFICE -> 30;
case OCR_MY_PDF -> 30;
case PYTHON_OPENCV -> 30;
case GHOSTSCRIPT -> 5;
case WEASYPRINT -> 30;
case INSTALL_APP -> 60;
case CALIBRE -> 30;
};
return new ProcessExecutor(semaphoreLimit, liveUpdates, timeoutMinutes);
}); });
} }
private final Semaphore semaphore; private final Semaphore semaphore;
private final boolean liveUpdates;
private long timeoutDuration;
private ProcessExecutor(int semaphoreLimit) { private ProcessExecutor(int semaphoreLimit, boolean liveUpdates, long timeout) {
this.semaphore = new Semaphore(semaphoreLimit); this.semaphore = new Semaphore(semaphoreLimit);
this.liveUpdates = liveUpdates;
this.timeoutDuration = timeout;
} }
public ProcessExecutorResult runCommandWithOutputHandling(List<String> command) public ProcessExecutorResult runCommandWithOutputHandling(List<String> command)
@@ -52,12 +82,12 @@ public class ProcessExecutor {
public ProcessExecutorResult runCommandWithOutputHandling( public ProcessExecutorResult runCommandWithOutputHandling(
List<String> command, File workingDirectory) throws IOException, InterruptedException { List<String> command, File workingDirectory) throws IOException, InterruptedException {
int exitCode = 1;
String messages = ""; String messages = "";
int exitCode = 1;
semaphore.acquire(); semaphore.acquire();
try { try {
System.out.print("Running command: " + String.join(" ", command)); logger.info("Running command: " + String.join(" ", command));
ProcessBuilder processBuilder = new ProcessBuilder(command); ProcessBuilder processBuilder = new ProcessBuilder(command);
// Use the working directory if it's set // Use the working directory if it's set
@@ -81,7 +111,11 @@ public class ProcessExecutor {
String line; String line;
while ((line = errorReader.readLine()) != null) { while ((line = errorReader.readLine()) != null) {
errorLines.add(line); errorLines.add(line);
if (liveUpdates) logger.info(line);
} }
} catch (InterruptedIOException e) {
logger.warn(
"Error reader thread was interrupted due to timeout.");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -98,7 +132,11 @@ public class ProcessExecutor {
String line; String line;
while ((line = outputReader.readLine()) != null) { while ((line = outputReader.readLine()) != null) {
outputLines.add(line); outputLines.add(line);
if (liveUpdates) logger.info(line);
} }
} catch (InterruptedIOException e) {
logger.warn(
"Error reader thread was interrupted due to timeout.");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -108,29 +146,42 @@ public class ProcessExecutor {
outputReaderThread.start(); outputReaderThread.start();
// Wait for the conversion process to complete // Wait for the conversion process to complete
exitCode = process.waitFor(); boolean finished = process.waitFor(timeoutDuration, TimeUnit.MINUTES);
if (!finished) {
// Terminate the process
process.destroy();
// Interrupt the reader threads
errorReaderThread.interrupt();
outputReaderThread.interrupt();
throw new IOException("Process timeout exceeded.");
}
exitCode = process.exitValue();
// Wait for the reader threads to finish // Wait for the reader threads to finish
errorReaderThread.join(); errorReaderThread.join();
outputReaderThread.join(); outputReaderThread.join();
if (outputLines.size() > 0) { if (!liveUpdates) {
String outputMessage = String.join("\n", outputLines); if (outputLines.size() > 0) {
messages += outputMessage; String outputMessage = String.join("\n", outputLines);
System.out.println("Command output:\n" + outputMessage); messages += outputMessage;
} logger.info("Command output:\n" + outputMessage);
if (errorLines.size() > 0) {
String errorMessage = String.join("\n", errorLines);
messages += errorMessage;
System.out.println("Command error output:\n" + errorMessage);
if (exitCode != 0) {
throw new IOException(
"Command process failed with exit code "
+ exitCode
+ ". Error message: "
+ errorMessage);
} }
if (errorLines.size() > 0) {
String errorMessage = String.join("\n", errorLines);
messages += errorMessage;
logger.warn("Command error output:\n" + errorMessage);
if (exitCode != 0) {
throw new IOException(
"Command process failed with exit code "
+ exitCode
+ ". Error message: "
+ errorMessage);
}
}
} else if (exitCode != 0) {
throw new IOException("Command process failed with exit code " + exitCode);
} }
} finally { } finally {
semaphore.release(); semaphore.release();

View File

@@ -9,10 +9,14 @@ security:
loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts
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 (via robots.txt), 'false' to disallow 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) enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes)
customApplications:
installBookFormats: false # Installs Calibre for book format conversion (For non docker it must be manually downloaded but will need to be true to show in UI)
installAdvancedHtmlToPDF: false # DO NOT USE EXTERNALLY, NOT SAFE! Install wkHtmlToPDF (For non docker it must be manually downloaded but will need to be true to show in UI)
#ui: #ui:
# appName: exampleAppName # Application's visible name # appName: exampleAppName # Application's visible name
# homeDescription: I am a description # Short description or tagline shown on homepage. # homeDescription: I am a description # Short description or tagline shown on homepage.

View File

@@ -356,22 +356,22 @@
}, },
{ {
"moduleName": "org.apache.pdfbox:fontbox", "moduleName": "org.apache.pdfbox:fontbox",
"moduleUrl": "http://pdfbox.apache.org/", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "2.0.29", "moduleVersion": "2.0.30",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.apache.pdfbox:pdfbox", "moduleName": "org.apache.pdfbox:pdfbox",
"moduleUrl": "http://pdfbox.apache.org", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "2.0.29", "moduleVersion": "2.0.30",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.apache.pdfbox:xmpbox", "moduleName": "org.apache.pdfbox:xmpbox",
"moduleUrl": "http://pdfbox.apache.org", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "2.0.29", "moduleVersion": "2.0.30",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-book" viewBox="0 0 16 16">
<path d="M1 2.828c.885-.37 2.154-.769 3.388-.893 1.33-.134 2.458.063 3.112.752v9.746c-.935-.53-2.12-.603-3.213-.493-1.18.12-2.37.461-3.287.811zm7.5-.141c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783"/>
</svg>

After

Width:  |  Height:  |  Size: 770 B

View File

@@ -209,20 +209,21 @@ class PdfContainer {
async exportPdf() { async exportPdf() {
const pdfDoc = await PDFLib.PDFDocument.create(); const pdfDoc = await PDFLib.PDFDocument.create();
for (var i=0; i<this.pagesContainer.childNodes.length; i++) { const pageContainers = this.pagesContainer.querySelectorAll('.page-container'); // Select all .page-container elements
const img = this.pagesContainer.childNodes[i].querySelector("img"); for (var i = 0; i < pageContainers.length; i++) {
if (!img) continue; const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]) if (!img) continue;
const page = pages[0]; const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx])
const page = pages[0];
const rotation = img.style.rotate;
if (rotation) { const rotation = img.style.rotate;
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, '')); if (rotation) {
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle)) const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ''));
} page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle))
}
pdfDoc.addPage(page);
} pdfDoc.addPage(page);
}
const pdfBytes = await pdfDoc.save(); const pdfBytes = await pdfDoc.save();
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' }); const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(pdfBlob); const url = URL.createObjectURL(pdfBlob);

View File

@@ -39,12 +39,12 @@
<h4 th:text="#{changeCreds.changeUserAndPassword}">Change Username and password</h4> <h4 th:text="#{changeCreds.changeUserAndPassword}">Change Username and password</h4>
<form action="api/v1/user/change-username-and-password" method="post"> <form action="api/v1/user/change-username-and-password" method="post">
<div class="mb-3"> <div class="mb-3">
<label for="newUsername" th:text="#{changeCreds.newUsername}">New Username</label> <label for="username" th:text="#{changeCreds.newUsername}">New Username</label>
<input type="text" class="form-control" name="newUsername" id="newUsername" th:placeholder="${username}"> <input type="text" class="form-control" name="username" id="username" th:placeholder="${username}">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="currentPassword" th:text="#{changeCreds.oldPassword}">Old Password</label> <label for="password" th:text="#{changeCreds.oldPassword}">Old Password</label>
<input type="password" class="form-control" name="currentPassword" id="currentPasswordPassword" th:placeholder="#{changeCreds.oldPassword}"> <input type="password" class="form-control" name="password" id="password" th:placeholder="#{changeCreds.oldPassword}">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="newPassword" th:text="#{changeCreds.newPassword}">New Password</label> <label for="newPassword" th:text="#{changeCreds.newPassword}">New Password</label>

View File

@@ -0,0 +1,30 @@
<!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=#{BookToPDF.title}, header=#{BookToPDF.header})}"></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="#{BookToPDF.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/book/pdf}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{BookToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{BookToPDF.help}"></p>
<p class="mt-3" th:text="#{BookToPDF.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>

View File

@@ -0,0 +1,55 @@
<!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=#{PDFToBook.title}, header=#{PDFToBook.header})}"></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="#{PDFToBook.header}"></h2>
<form method="post" enctype="multipart/form-data"
th:action="@{api/v1/convert/pdf/book}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToBook.selectText.1}"></label> <select
class="form-control" name="outputFormat">
<option value="epub">EPUB</option>
<option value="mobi">MOBI</option>
<option value="azw3">AZW3</option>
<option value="docx">DOCX</option>
<option value="rtf">RTF</option>
<option value="txt">TXT</option>
<option value="html">HTML</option>
<option value="lit">LIT</option>
<option value="fb2">FB2</option>
<option value="pdb">PDB</option>
<option value="lrf">LRF</option>
</select>
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{PDFToBook.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToBook.help}"></p>
<p class="mt-3" th:text="#{PDFToBook.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -77,6 +77,7 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('html-to-pdf', 'images/html.svg', 'home.HTMLToPDF.title', 'home.HTMLToPDF.desc', 'HTMLToPDF.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('html-to-pdf', 'images/html.svg', 'home.HTMLToPDF.title', 'home.HTMLToPDF.desc', 'HTMLToPDF.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('url-to-pdf', 'images/url.svg', 'home.URLToPDF.title', 'home.URLToPDF.desc', 'URLToPDF.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('url-to-pdf', 'images/url.svg', 'home.URLToPDF.title', 'home.URLToPDF.desc', 'URLToPDF.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('markdown-to-pdf', 'images/markdown.svg', 'home.MarkdownToPDF.title', 'home.MarkdownToPDF.desc', 'MarkdownToPDF.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('markdown-to-pdf', 'images/markdown.svg', 'home.MarkdownToPDF.title', 'home.MarkdownToPDF.desc', 'MarkdownToPDF.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('book-to-pdf', 'images/book.svg', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags')}"></div>
<hr class="dropdown-divider"> <hr class="dropdown-divider">
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-img', 'images/image.svg', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-img', 'images/image.svg', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-word', 'images/file-earmark-word.svg', 'home.PDFToWord.title', 'home.PDFToWord.desc', 'PDFToWord.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-word', 'images/file-earmark-word.svg', 'home.PDFToWord.title', 'home.PDFToWord.desc', 'PDFToWord.tags')}"></div>
@@ -86,6 +87,7 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-xml', 'images/filetype-xml.svg', 'home.PDFToXML.title', 'home.PDFToXML.desc', 'PDFToXML.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-xml', 'images/filetype-xml.svg', 'home.PDFToXML.title', 'home.PDFToXML.desc', 'PDFToXML.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-pdfa', 'images/file-earmark-pdf.svg', 'home.pdfToPDFA.title', 'home.pdfToPDFA.desc', 'pdfToPDFA.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-pdfa', 'images/file-earmark-pdf.svg', 'home.pdfToPDFA.title', 'home.pdfToPDFA.desc', 'pdfToPDFA.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-csv', 'images/pdf-csv.svg', 'home.tableExtraxt.title', 'home.tableExtraxt.desc', 'pdfToPDFA.tags')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-csv', 'images/pdf-csv.svg', 'home.tableExtraxt.title', 'home.tableExtraxt.desc', 'pdfToPDFA.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-book', 'images/book.svg', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags')}"></div>
</div> </div>
</li> </li>

View File

@@ -99,6 +99,8 @@
<div th:replace="~{fragments/card :: card(id='split-by-size-or-count', cardTitle=#{home.autoSizeSplitPDF.title}, cardText=#{home.autoSizeSplitPDF.desc}, cardLink='split-by-size-or-count', svgPath='images/layout-split.svg')}"></div> <div th:replace="~{fragments/card :: card(id='split-by-size-or-count', cardTitle=#{home.autoSizeSplitPDF.title}, cardText=#{home.autoSizeSplitPDF.desc}, cardLink='split-by-size-or-count', svgPath='images/layout-split.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='overlay-pdf', cardTitle=#{home.overlay-pdfs.title}, cardText=#{home.overlay-pdfs.desc}, cardLink='overlay-pdf', svgPath='images/overlay.svg')}"></div> <div th:replace="~{fragments/card :: card(id='overlay-pdf', cardTitle=#{home.overlay-pdfs.title}, cardText=#{home.overlay-pdfs.desc}, cardLink='overlay-pdf', svgPath='images/overlay.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='split-pdf-by-sections', cardTitle=#{home.split-by-sections.title}, cardText=#{home.split-by-sections.desc}, cardLink='split-pdf-by-sections', svgPath='images/layout-split.svg')}"></div> <div th:replace="~{fragments/card :: card(id='split-pdf-by-sections', cardTitle=#{home.split-by-sections.title}, cardText=#{home.split-by-sections.desc}, cardLink='split-pdf-by-sections', svgPath='images/layout-split.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='book-to-pdf', cardTitle=#{home.BookToPDF.title}, cardText=#{home.BookToPDF.desc}, cardLink='book-to-pdf', svgPath='images/book.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-book', cardTitle=#{home.PDFToBook.title}, cardText=#{home.PDFToBook.desc}, cardLink='pdf-to-book', svgPath='images/book.svg')}"></div>