mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-10-25 11:17:28 +02:00 
			
		
		
		
	compress finalise, cert test, locale test
This commit is contained in:
		
							parent
							
								
									9d80458250
								
							
						
					
					
						commit
						3cad43006a
					
				| @ -24,8 +24,9 @@ dependencies { | ||||
|      | ||||
| 	//general PDF | ||||
|     implementation 'org.apache.pdfbox:pdfbox:2.0.28' | ||||
|      | ||||
|      | ||||
|     implementation 'org.bouncycastle:bcprov-jdk15on:1.70' | ||||
|     implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'  | ||||
|     implementation 'com.itextpdf:itext7-core:7.2.5' | ||||
|     implementation 'org.springframework.boot:spring-boot-starter-actuator' | ||||
|     implementation 'io.micrometer:micrometer-core' | ||||
|      | ||||
|  | ||||
| @ -2,8 +2,10 @@ package stirling.software.SPDF.config; | ||||
| 
 | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| import org.springframework.context.MessageSource; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.context.support.ReloadableResourceBundleMessageSource; | ||||
| import org.springframework.web.servlet.LocaleResolver; | ||||
| import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||||
| import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||||
| @ -50,4 +52,15 @@ public class Beans implements WebMvcConfigurer { | ||||
|         return slr; | ||||
|     } | ||||
| 
 | ||||
|     @Bean | ||||
|     public MessageSource messageSource() { | ||||
|         ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); | ||||
|         //messageSource.setBasename("classpath:messages"); | ||||
|         messageSource.setDefaultEncoding("UTF-8"); | ||||
|         messageSource.setUseCodeAsDefaultMessage(true); | ||||
|         messageSource.setDefaultLocale(Locale.UK); // setting default locale | ||||
|         return messageSource; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -56,8 +56,10 @@ public class CompressController { | ||||
|         } | ||||
| 
 | ||||
|         Long expectedOutputSize = 0L; | ||||
|         if (expectedOutputSizeString != null) { | ||||
|         boolean autoMode = false; | ||||
|         if (expectedOutputSizeString != null && expectedOutputSizeString.length() > 1 ) { | ||||
|             expectedOutputSize = PdfUtils.convertSizeToBytes(expectedOutputSizeString); | ||||
|             autoMode = true; | ||||
|         } | ||||
| 
 | ||||
|         // Save the uploaded file to a temporary location | ||||
| @ -69,8 +71,8 @@ public class CompressController { | ||||
|         // Prepare the output file path | ||||
|         Path tempOutputFile = Files.createTempFile("output_", ".pdf"); | ||||
| 
 | ||||
|         // Determine initial optimization level based on expected size reduction, only if optimizeLevel is not provided | ||||
|         if(optimizeLevel == null) { | ||||
|         // Determine initial optimization level based on expected size reduction, only if in autoMode | ||||
|         if(autoMode) { | ||||
|             double sizeReductionRatio = expectedOutputSize / (double) inputFileSize; | ||||
|             if (sizeReductionRatio > 0.7) { | ||||
|                 optimizeLevel = 1; | ||||
| @ -79,12 +81,12 @@ public class CompressController { | ||||
|             } else if (sizeReductionRatio > 0.35) { | ||||
|                 optimizeLevel = 3; | ||||
|             } else { | ||||
|                 optimizeLevel = 4; | ||||
|                 optimizeLevel = 3; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         boolean sizeMet = expectedOutputSize == 0L; | ||||
|         while (!sizeMet && optimizeLevel <= 5) { | ||||
|         boolean sizeMet = false; | ||||
|         while (!sizeMet && optimizeLevel <= 4) { | ||||
|             // Prepare the Ghostscript command | ||||
|             List<String> command = new ArrayList<>(); | ||||
|             command.add("gs"); | ||||
| @ -99,12 +101,9 @@ public class CompressController { | ||||
|                 command.add("-dPDFSETTINGS=/printer"); | ||||
|                 break;     | ||||
|             case 3: | ||||
|                 command.add("-dPDFSETTINGS=/default"); | ||||
|                 break; | ||||
|             case 4: | ||||
|                 command.add("-dPDFSETTINGS=/ebook"); | ||||
|                 break; | ||||
|             case 5: | ||||
|             case 4: | ||||
|                 command.add("-dPDFSETTINGS=/screen"); | ||||
|                 break; | ||||
|             default: | ||||
| @ -119,20 +118,27 @@ public class CompressController { | ||||
| 
 | ||||
|             int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT).runCommandWithOutputHandling(command); | ||||
| 
 | ||||
|             // Check if file size is within expected size | ||||
|             // Check if file size is within expected size or not auto mode so instantly finish | ||||
|             long outputFileSize = Files.size(tempOutputFile); | ||||
|             if (outputFileSize <= expectedOutputSize) { | ||||
|             if (outputFileSize <= expectedOutputSize || !autoMode) { | ||||
|                 sizeMet = true; | ||||
|             } else { | ||||
|                 // Increase optimization level for next iteration | ||||
|                 optimizeLevel++; | ||||
|                 System.out.println("Increasing ghostscript optimisation level to " + optimizeLevel); | ||||
|                 if(autoMode && optimizeLevel > 3) { | ||||
|                     System.out.println("Skipping level 4 due to bad results in auto mode"); | ||||
|                     sizeMet = true; | ||||
|                 } else if(optimizeLevel == 5) { | ||||
|                      | ||||
|                 } else { | ||||
|                     System.out.println("Increasing ghostscript optimisation level to " + optimizeLevel); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|          | ||||
| 
 | ||||
|         if (expectedOutputSize != null) { | ||||
|         if (expectedOutputSize != null && autoMode) { | ||||
|             long outputFileSize = Files.size(tempOutputFile); | ||||
|             if (outputFileSize > expectedOutputSize) { | ||||
|                 try (PDDocument doc = PDDocument.load(new File(tempOutputFile.toString()))) { | ||||
|  | ||||
| @ -0,0 +1,144 @@ | ||||
| package stirling.software.SPDF.controller.api.security; | ||||
| 
 | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.security.KeyStore; | ||||
| import java.security.KeyStoreException; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.security.NoSuchProviderException; | ||||
| import java.security.Principal; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.Security; | ||||
| import java.security.UnrecoverableKeyException; | ||||
| import java.security.cert.Certificate; | ||||
| import java.security.cert.CertificateEncodingException; | ||||
| import java.security.cert.CertificateException; | ||||
| import java.security.cert.X509Certificate; | ||||
| import java.security.spec.PKCS8EncodedKeySpec; | ||||
| import java.util.Arrays; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import javax.naming.ldap.LdapName; | ||||
| import javax.naming.ldap.Rdn; | ||||
| 
 | ||||
| import org.apache.pdfbox.pdmodel.PDDocument; | ||||
| import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; | ||||
| import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; | ||||
| import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions; | ||||
| import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties; | ||||
| import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSignDesigner; | ||||
| import org.bouncycastle.jce.provider.BouncyCastleProvider; | ||||
| import org.bouncycastle.util.io.pem.PemReader; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.web.bind.annotation.PostMapping; | ||||
| import org.springframework.web.bind.annotation.RequestParam; | ||||
| import org.springframework.web.bind.annotation.RequestPart; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
| 
 | ||||
| import com.itextpdf.kernel.pdf.*; | ||||
| import com.itextpdf.signatures.*; | ||||
| import org.bouncycastle.jce.provider.BouncyCastleProvider; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.web.bind.annotation.*; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
| 
 | ||||
| import java.io.*; | ||||
| import java.security.*; | ||||
| import java.security.cert.Certificate; | ||||
| import java.security.cert.CertificateFactory; | ||||
| import java.security.cert.X509Certificate; | ||||
| 
 | ||||
| 
 | ||||
| @RestController | ||||
| public class CertSignController { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(CertSignController.class); | ||||
| 
 | ||||
|     static { | ||||
|         Security.addProvider(new BouncyCastleProvider()); | ||||
|     } | ||||
| 
 | ||||
|     @PostMapping(consumes = "multipart/form-data", value = "/cert-sign") | ||||
|     public ResponseEntity<String> signPDF( | ||||
|             @RequestParam("pdf") MultipartFile pdf, | ||||
|             @RequestParam(value = "key", required = false) MultipartFile privateKeyFile, | ||||
|             @RequestParam(value = "cert", required = false) MultipartFile certFile, | ||||
|             @RequestParam(value = "p12", required = false) MultipartFile p12File, | ||||
|             @RequestParam(value = "password", required = false) String password) throws Exception { | ||||
|         BouncyCastleProvider provider = new BouncyCastleProvider(); | ||||
|         Security.addProvider(provider); | ||||
| 
 | ||||
|         PrivateKey privateKey = null; | ||||
|         X509Certificate cert = null; | ||||
| 
 | ||||
|         if (p12File != null) { | ||||
|             KeyStore ks = KeyStore.getInstance("PKCS12"); | ||||
|             ks.load(new ByteArrayInputStream(p12File.getBytes()), password.toCharArray()); | ||||
|             String alias = ks.aliases().nextElement(); | ||||
|             privateKey = (PrivateKey) ks.getKey(alias, password.toCharArray()); | ||||
|             cert = (X509Certificate) ks.getCertificate(alias); | ||||
|         } else { | ||||
|             // Load private key | ||||
|             KeyFactory keyFactory = KeyFactory.getInstance("RSA", provider); | ||||
|             if (isPEM(privateKeyFile.getBytes())) { | ||||
|                 privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(parsePEM(privateKeyFile.getBytes()))); | ||||
|             } else { | ||||
|                 privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyFile.getBytes())); | ||||
|             } | ||||
| 
 | ||||
|             // Load certificate | ||||
|             CertificateFactory certFactory = CertificateFactory.getInstance("X.509", provider); | ||||
|             if (isPEM(certFile.getBytes())) { | ||||
|                 cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(parsePEM(certFile.getBytes()))); | ||||
|             } else { | ||||
|                 cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(certFile.getBytes())); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Set up the PDF reader and stamper | ||||
|         PdfReader reader = new PdfReader(new ByteArrayInputStream(pdf.getBytes())); | ||||
|         ByteArrayOutputStream signedPdf = new ByteArrayOutputStream(); | ||||
|         PdfSigner signer = new PdfSigner(reader, signedPdf, new StampingProperties()); | ||||
| 
 | ||||
|         // Set up the signing appearance | ||||
|         PdfSignatureAppearance appearance = signer.getSignatureAppearance() | ||||
|                 .setReason("Test") | ||||
|                 .setLocation("TestLocation"); | ||||
| 
 | ||||
|         // Set up the signer | ||||
|         PrivateKeySignature pks = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256, provider.getName()); | ||||
|         IExternalSignature pss = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256, provider.getName()); | ||||
|         IExternalDigest digest = new BouncyCastleDigest(); | ||||
| 
 | ||||
|         // Call iTex7 to sign the PDF | ||||
|         signer.signDetached(digest, pks, new Certificate[] {cert}, null, null, null, 0, PdfSigner.CryptoStandard.CMS); | ||||
| 
 | ||||
|         // This is just an example, you might want to save this signed PDF into your system or send it back in the response. | ||||
|         // For simplicity, we will just print out the size of the signed PDF. | ||||
|         System.out.println("Signed PDF size: " + signedPdf.size()); | ||||
| 
 | ||||
|         return ResponseEntity.ok("Signed PDF successfully"); | ||||
|     } | ||||
| 
 | ||||
|     private byte[] parsePEM(byte[] content) throws IOException { | ||||
|         PemReader pemReader = new PemReader(new InputStreamReader(new ByteArrayInputStream(content))); | ||||
|         return pemReader.readPemObject().getContent(); | ||||
|     } | ||||
| 
 | ||||
|     private boolean isPEM(byte[] content) { | ||||
|         String contentStr = new String(content); | ||||
|         return contentStr.contains("-----BEGIN") && contentStr.contains("-----END"); | ||||
|     } | ||||
|     | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -297,11 +297,11 @@ public class PdfUtils { | ||||
|         sizeStr = sizeStr.trim().toUpperCase(); | ||||
|         try { | ||||
|             if (sizeStr.endsWith("KB")) { | ||||
|                 return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 2)) * 1024; | ||||
|                 return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024); | ||||
|             } else if (sizeStr.endsWith("MB")) { | ||||
|                 return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024; | ||||
|                 return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024); | ||||
|             } else if (sizeStr.endsWith("GB")) { | ||||
|                 return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024 * 1024; | ||||
|                 return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024 * 1024); | ||||
|             } else if (sizeStr.endsWith("B")) { | ||||
|                 return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1)); | ||||
|             } else { | ||||
| @ -313,5 +313,6 @@ public class PdfUtils { | ||||
|          | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -223,8 +223,11 @@ fileToPDF.submit=Convert to PDF | ||||
| compress.title=Compress | ||||
| compress.header=Compress PDF | ||||
| compress.credit=This service uses OCRmyPDF for PDF Compress/Optimisation. | ||||
| compress.selectText.1=Optimization level: | ||||
| compress.selectText.2=Expected PDF Size (e.g. 100MB, 25KB, 500B)  | ||||
| compress.selectText.1=Manual Mode - From 1 to 4 | ||||
| compress.selectText.2=Optimization level: | ||||
| compress.selectText.3=4 (Terrible for text images) | ||||
| compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size | ||||
| compress.selectText.5=Expected PDF Size (e.g. 25MB, 10.8MB, 25KB)  | ||||
| compress.submit=Compress | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -11,7 +11,7 @@ | ||||
|         <div id="content-wrap"> | ||||
|             <div th:insert="~{fragments/navbar.html :: navbar}"></div> | ||||
|             <br> <br> | ||||
|             <div class="container">R | ||||
|             <div class="container"> | ||||
|                 <div class="row justify-content-center"> | ||||
|                     <div class="col-md-6"> | ||||
|                         <h2 th:text="#{compress.header}"></h2> | ||||
| @ -19,22 +19,21 @@ | ||||
|                             <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> | ||||
|                             <div class="card mb-3"> | ||||
|                                 <div class="card-body"> | ||||
|                                     <h4>Manual Mode - From 1 to 5</h4> | ||||
|                                     <label for="optimizeLevel" th:text="#{compress.selectText.1}"></label> | ||||
|                                     <h4 th:text="#{compress.selectText.1}"></h4> | ||||
|                                     <label for="optimizeLevel" th:text="#{compress.selectText.2}"></label> | ||||
|                                     <select name="optimizeLevel" id="optimizeLevel" class="form-control"> | ||||
|                                         <option value="1">1</option> | ||||
|                                         <option value="2" selected>2</option> | ||||
|                                         <option value="3">3</option> | ||||
|                                         <option value="4">4</option> | ||||
|                                         <option value="5">5</option> | ||||
|                                         <option value="4" th:text="#{compress.selectText.3}"></option> | ||||
|                                     </select> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                              | ||||
|                             <div class="card mb-3"> | ||||
|                                 <div class="card-body"> | ||||
|                                     <h4>Auto mode - Auto adjusts quality to get PDF to exact size</h4> | ||||
|                                     <label for="expectedOutputSize" th:text="#{compress.selectText.2}"></label> | ||||
|                                     <h4 th:text="#{compress.selectText.4}"></h4> | ||||
|                                     <label for="expectedOutputSize" th:text="#{compress.selectText.5}"></label> | ||||
|                                     <input type="text" name="expectedOutputSize" id="expectedOutputSize" min="1" class="form-control"> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user