Compare commits

...

6 Commits

Author SHA1 Message Date
dependabot[bot] 20c66ac19b
Merge 86971e7dd5 into 4fea8d10f8 2024-04-27 10:04:52 +00:00
dependabot[bot] 86971e7dd5
Bump org.bouncycastle:bcprov-jdk18on from 1.77 to 1.78.1
Bumps [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java) from 1.77 to 1.78.1.
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

---
updated-dependencies:
- dependency-name: org.bouncycastle:bcprov-jdk18on
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-27 10:04:50 +00:00
github-actions[bot] 4fea8d10f8
💾 Update Version (#1130)
💾 Sync Versions
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-04-27 11:04:28 +01:00
Anthony Stirling 8c9d6f7b66
Custom HTML support #355 (#1129)
* test

* settings

* version
2024-04-27 11:03:57 +01:00
Anthony Stirling 30444fc9bb
commit (#1128)
* commit

* formatting
2024-04-26 23:27:40 +01:00
Han 70349d642b
Update messages_tr_TR.properties (#1126)
* Update messages_tr_TR.properties

* Update messages_tr_TR.properties
2024-04-26 19:37:09 +01:00
12 changed files with 129 additions and 36 deletions

View File

@ -201,7 +201,7 @@ Stirling PDF allows easy customization of the app.
Includes things like Includes things like
- Custom application name - Custom application name
- Custom slogans, icons, images, and even custom HTML (via file overrides) - Custom slogans, icons, HTML, images CSS etc (via file overrides)
There are two options for this, either using the generated settings file ``settings.yml`` There are two options for this, either using the generated settings file ``settings.yml``
This file is located in the ``/configs`` directory and follows standard YAML formatting This file is located in the ``/configs`` directory and follows standard YAML formatting
@ -229,6 +229,7 @@ system:
customStaticFilePath: '/customFiles/static/' # Directory path for custom static files customStaticFilePath: '/customFiles/static/' # Directory path for custom static files
showUpdate: true # see when a new update is available showUpdate: true # see when a new update is available
showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true' showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template html files
#ui: #ui:
# appName: exampleAppName # Application's visible name # appName: exampleAppName # Application's visible name

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.23.0' version = '0.23.1'
sourceCompatibility = '17' sourceCompatibility = '17'
repositories { repositories {
@ -145,7 +145,7 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'commons-logging', module: 'commons-logging'
} }
implementation 'org.bouncycastle:bcprov-jdk18on:1.77' implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77' implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.4' implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.4'
implementation 'io.micrometer:micrometer-core:1.12.4' implementation 'io.micrometer:micrometer-core:1.12.4'

View File

@ -1,5 +1,5 @@
apiVersion: v2 apiVersion: v2
appVersion: 0.23.0 appVersion: 0.23.1
description: locally hosted web application that allows you to perform various operations description: locally hosted web application that allows you to perform various operations
on PDF files on PDF files
home: https://github.com/Stirling-Tools/Stirling-PDF home: https://github.com/Stirling-Tools/Stirling-PDF

View File

@ -7,18 +7,34 @@ import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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.context.annotation.Lazy;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.spring6.SpringTemplateEngine;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
@Configuration @Configuration
@Lazy
public class AppConfig { public class AppConfig {
@Autowired ApplicationProperties applicationProperties; @Autowired ApplicationProperties applicationProperties;
@Bean
@ConditionalOnProperty(
name = "system.customHTMLFiles",
havingValue = "true",
matchIfMissing = false)
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
return templateEngine;
}
@Bean(name = "loginEnabled") @Bean(name = "loginEnabled")
public boolean loginEnabled() { public boolean loginEnabled() {
return applicationProperties.getSecurity().getEnableLogin(); return applicationProperties.getSecurity().getEnableLogin();

View File

@ -0,0 +1,48 @@
package stirling.software.SPDF.config;
import java.io.IOException;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
import org.thymeleaf.templateresource.ClassLoaderTemplateResource;
import org.thymeleaf.templateresource.FileTemplateResource;
import org.thymeleaf.templateresource.ITemplateResource;
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
private final ResourceLoader resourceLoader;
public FileFallbackTemplateResolver(ResourceLoader resourceLoader) {
super();
this.resourceLoader = resourceLoader;
setSuffix(".html");
}
// Note this does not work in local IDE, Prod jar only.
@Override
protected ITemplateResource computeTemplateResource(
IEngineConfiguration configuration,
String ownerTemplate,
String template,
String resourceName,
String characterEncoding,
Map<String, Object> templateResolutionAttributes) {
Resource resource =
resourceLoader.getResource("file:./customFiles/templates/" + resourceName);
try {
if (resource.exists() && resource.isReadable()) {
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
}
} catch (IOException e) {
}
return new ClassLoaderTemplateResource(
Thread.currentThread().getContextClassLoader(),
"classpath:/templates/" + resourceName,
characterEncoding);
}
}

View File

@ -37,6 +37,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames; import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -51,7 +52,9 @@ public class FakeScanControllerWIP {
private static final Logger logger = LoggerFactory.getLogger(FakeScanControllerWIP.class); private static final Logger logger = LoggerFactory.getLogger(FakeScanControllerWIP.class);
// TODO finish
@PostMapping(consumes = "multipart/form-data", value = "/fake-scan") @PostMapping(consumes = "multipart/form-data", value = "/fake-scan")
@Hidden
@Operation( @Operation(
summary = "Repair a PDF file", summary = "Repair a PDF file",
description = description =
@ -94,14 +97,13 @@ public class FakeScanControllerWIP {
public BufferedImage processImage(BufferedImage image) { public BufferedImage processImage(BufferedImage image) {
// Rotation // Rotation
addDustAndHairs(image, 50); image = softenEdges(image, 50);
// image = rotate(image, 1); image = rotate(image, 1);
// image = softenEdges(image, 5);
image = applyGaussianBlur(image, 0.5); image = applyGaussianBlur(image, 0.5);
addGaussianNoise(image, 0.8); addGaussianNoise(image, 0.5);
image = linearStretch(image); image = linearStretch(image);
addDustAndHairs(image, 3);
return image; return image;
} }
@ -156,32 +158,51 @@ public class FakeScanControllerWIP {
g2.drawImage(image, 0, 0, null); g2.drawImage(image, 0, 0, null);
g2.setComposite(AlphaComposite.DstIn); g2.setComposite(AlphaComposite.DstIn);
g2.setPaint(
new GradientPaint( // Top edge
0, 0, new Color(0, 0, 0, 1f), 0, featherRadius, new Color(0, 0, 0, 0f)));
g2.fillRect(0, 0, width, featherRadius); // top edge
g2.setPaint( g2.setPaint(
new GradientPaint( new GradientPaint(
0, 0,
height - featherRadius, 0,
new Color(0, 0, 0, 1f),
0,
featherRadius * 2,
new Color(0, 0, 0, 0f)));
g2.fillRect(0, 0, width, featherRadius);
// Bottom edge
g2.setPaint(
new GradientPaint(
0,
height - featherRadius * 2,
new Color(0, 0, 0, 0f), new Color(0, 0, 0, 0f),
0, 0,
height, height,
new Color(0, 0, 0, 1f))); new Color(0, 0, 0, 1f)));
g2.fillRect(0, height - featherRadius, width, featherRadius); // bottom edge g2.fillRect(0, height - featherRadius, width, featherRadius);
// Left edge
g2.setPaint( g2.setPaint(
new GradientPaint( new GradientPaint(
0, 0, new Color(0, 0, 0, 1f), featherRadius, 0, new Color(0, 0, 0, 0f))); 0,
g2.fillRect(0, 0, featherRadius, height); // left edge 0,
new Color(0, 0, 0, 1f),
featherRadius * 2,
0,
new Color(0, 0, 0, 0f)));
g2.fillRect(0, 0, featherRadius, height);
// Right edge
g2.setPaint( g2.setPaint(
new GradientPaint( new GradientPaint(
width - featherRadius, width - featherRadius * 2,
0, 0,
new Color(0, 0, 0, 0f), new Color(0, 0, 0, 0f),
width, width,
0, 0,
new Color(0, 0, 0, 1f))); new Color(0, 0, 0, 1f)));
g2.fillRect(width - featherRadius, 0, featherRadius, height); // right edge g2.fillRect(width - featherRadius, 0, featherRadius, height);
g2.dispose(); g2.dispose();
return output; return output;

View File

@ -20,12 +20,10 @@ import org.apache.pdfbox.printing.PDFPageable;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
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.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import 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.misc.PrintFileRequest; import stirling.software.SPDF.model.api.misc.PrintFileRequest;
@ -35,12 +33,13 @@ import stirling.software.SPDF.model.api.misc.PrintFileRequest;
@Tag(name = "Misc", description = "Miscellaneous APIs") @Tag(name = "Misc", description = "Miscellaneous APIs")
public class PrintFileController { public class PrintFileController {
//TODO // TODO
//@PostMapping(value = "/print-file", consumes = "multipart/form-data") // @PostMapping(value = "/print-file", consumes = "multipart/form-data")
//@Operation( // @Operation(
// summary = "Prints PDF/Image file to a set printer", // summary = "Prints PDF/Image file to a set printer",
// description = // description =
// "Input of PDF or Image along with a printer name/URL/IP to match against to send it to (Fire and forget) Input:Any Output:N/A Type:SISO") // "Input of PDF or Image along with a printer name/URL/IP to match against to
// send it to (Fire and forget) Input:Any Output:N/A Type:SISO")
public ResponseEntity<String> printFile(@ModelAttribute PrintFileRequest request) public ResponseEntity<String> printFile(@ModelAttribute PrintFileRequest request)
throws IOException { throws IOException {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();

View File

@ -212,6 +212,15 @@ public class ApplicationProperties {
private Integer maxFileSize; private Integer maxFileSize;
private boolean showUpdate; private boolean showUpdate;
private Boolean showUpdateOnlyAdmin; private Boolean showUpdateOnlyAdmin;
private boolean customHTMLFiles;
public boolean isCustomHTMLFiles() {
return customHTMLFiles;
}
public void setCustomHTMLFiles(boolean customHTMLFiles) {
this.customHTMLFiles = customHTMLFiles;
}
public boolean getShowUpdateOnlyAdmin() { public boolean getShowUpdateOnlyAdmin() {
return showUpdateOnlyAdmin; return showUpdateOnlyAdmin;

View File

@ -342,8 +342,6 @@ public class PdfUtils {
boolean imageIsLandscape = image.getWidth() > image.getHeight(); boolean imageIsLandscape = image.getWidth() > image.getHeight();
PDRectangle pageSize = PDRectangle.A4; PDRectangle pageSize = PDRectangle.A4;
System.out.println(fitOption);
if (autoRotate && imageIsLandscape) { if (autoRotate && imageIsLandscape) {
pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth()); pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth());
} }

View File

@ -66,7 +66,7 @@ seeDockerHub=Docker Hub'a bakın
visitGithub=Github Deposunu Ziyaret Edin visitGithub=Github Deposunu Ziyaret Edin
donate=Bağış Yapın donate=Bağış Yapın
color=Renk color=Renk
sponsor=Sponsor sponsor=Bağış
@ -112,7 +112,7 @@ navbar.settings=Ayarlar
############# #############
settings.title=Ayarlar settings.title=Ayarlar
settings.update=Güncelleme mevcut settings.update=Güncelleme mevcut
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.updateAvailable={0} mevcut kurulu sürümdür. Yeni bir sürüm ({1}) mevcuttur.
settings.appVersion=Uygulama Sürümü: settings.appVersion=Uygulama Sürümü:
settings.downloadOption.title=İndirme seçeneği seçin (Zip olmayan tek dosya indirmeler için): settings.downloadOption.title=İndirme seçeneği seçin (Zip olmayan tek dosya indirmeler için):
settings.downloadOption.1=Aynı pencerede aç settings.downloadOption.1=Aynı pencerede aç
@ -666,7 +666,7 @@ BookToPDF.submit=Dönüştür
#PDFToBook #PDFToBook
PDFToBook.title=PDF'den Kitaba PDFToBook.title=PDF'den Kitaba
PDFToBook.header=PDF'den Kitaba PDFToBook.header=PDF'den Kitaba
PDFToBook.selectText.1=Format PDFToBook.selectText.1=Format biçimi
PDFToBook.credit=Kalibre Kullanır PDFToBook.credit=Kalibre Kullanır
PDFToBook.submit=Dönüştür PDFToBook.submit=Dönüştür
@ -941,7 +941,7 @@ pdfToPDFA.header=PDF'den PDF/A'ya
pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için OCRmyPDF kullanır pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için OCRmyPDF kullanır
pdfToPDFA.submit=Dönüştür pdfToPDFA.submit=Dönüştür
pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Çıkış formatı
#PDFToWord #PDFToWord

View File

@ -14,7 +14,8 @@ system:
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)
showUpdate: true # see when a new update is available showUpdate: true # see when a new update is available
showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true' showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
customHTMLFiles: false # Enable to have files placed in /customFiles/templates override the existing template html files
#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

@ -17,20 +17,20 @@
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto flex-nowrap"> <ul class="navbar-nav me-auto flex-nowrap">
<li class="nav-item"> <li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('multi-tool')}">
<a class="nav-link" href="#" th:href="@{multi-tool}" th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}"> <a class="nav-link" href="#" th:href="@{multi-tool}" th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}">
<img class="icon" src="images/tools.svg" alt="icon"> <img class="icon" src="images/tools.svg" alt="icon">
<span class="icon-text" th:text="#{home.multiTool.title}"></span> <span class="icon-text" th:text="#{home.multiTool.title}"></span>
</a> </a>
</li> </li>
<li class="nav-item nav-item-separator"></li> <li th:if="${@endpointConfiguration.isEndpointEnabled('multi-tool')}" class="nav-item nav-item-separator"></li>
<li class="nav-item"> <li th:if="${@endpointConfiguration.isEndpointEnabled('pipeline')}" class="nav-item">
<a class="nav-link" href="#" th:href="@{pipeline}" th:classappend="${currentPage}=='pipeline' ? 'active' : ''" th:title="#{home.pipeline.desc}"> <a class="nav-link" href="#" th:href="@{pipeline}" th:classappend="${currentPage}=='pipeline' ? 'active' : ''" th:title="#{home.pipeline.desc}">
<img class="icon" src="images/pipeline.svg" alt="icon"> <img class="icon" src="images/pipeline.svg" alt="icon">
<span class="icon-text" th:text="#{home.pipeline.title}"></span> <span class="icon-text" th:text="#{home.pipeline.title}"></span>
</a> </a>
</li> </li>
<li class="nav-item nav-item-separator"></li> <li th:if="${@endpointConfiguration.isEndpointEnabled('pipeline')}" class="nav-item nav-item-separator"></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='merge-pdfs' OR ${currentPage}=='split-pdfs' OR ${currentPage}=='crop' OR ${currentPage}=='adjust-contrast' OR ${currentPage}=='pdf-organizer' OR ${currentPage}=='rotate-pdf' OR ${currentPage}=='multi-page-layout' OR ${currentPage}=='scale-pages' OR ${currentPage}=='auto-split-pdf' OR ${currentPage}=='extract-page' OR ${currentPage}=='pdf-to-single-page' ? 'active' : ''"> <li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='merge-pdfs' OR ${currentPage}=='split-pdfs' OR ${currentPage}=='crop' OR ${currentPage}=='adjust-contrast' OR ${currentPage}=='pdf-organizer' OR ${currentPage}=='rotate-pdf' OR ${currentPage}=='multi-page-layout' OR ${currentPage}=='scale-pages' OR ${currentPage}=='auto-split-pdf' OR ${currentPage}=='extract-page' OR ${currentPage}=='pdf-to-single-page' ? 'active' : ''">
<a class="nav-link dropdown-toggle" id="navbarDropdown-1" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link dropdown-toggle" id="navbarDropdown-1" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="icon" src="images/file-earmark-pdf.svg" alt="icon"> <img class="icon" src="images/file-earmark-pdf.svg" alt="icon">