Skip to content

Commit

Permalink
auto split init
Browse files Browse the repository at this point in the history
  • Loading branch information
Frooodle committed Jul 15, 2023
1 parent ddf5915 commit 6e32c7f
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 1 deletion.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-core'

implementation group: 'com.google.zxing', name: 'core', version: '3.5.1'

developmentOnly("org.springframework.boot:spring-boot-devtools")

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package stirling.software.SPDF.controller.api.other;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.http.MediaType;
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.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;

import stirling.software.SPDF.utils.WebResponseUtils;

@RestController
public class AutoSplitPdfController {

private static final String QR_CONTENT = "https://github.com/Frooodle/Stirling-PDF";

@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
public ResponseEntity<byte[]> autoSplitPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
InputStream inputStream = file.getInputStream();
PDDocument document = PDDocument.load(inputStream);
PDFRenderer pdfRenderer = new PDFRenderer(document);

List<PDDocument> splitDocuments = new ArrayList<>();
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); // create this list to store ByteArrayOutputStreams for zipping

for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150);
String result = decodeQRCode(bim);

if(QR_CONTENT.equals(result) && page != 0) {
splitDocuments.add(new PDDocument());
}

if (!splitDocuments.isEmpty() && !QR_CONTENT.equals(result)) {
splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page));
} else if (page == 0) {
PDDocument firstDocument = new PDDocument();
firstDocument.addPage(document.getPage(page));
splitDocuments.add(firstDocument);
}
}

// After all pages are added to splitDocuments, convert each to ByteArrayOutputStream and add to splitDocumentsBoas
for (PDDocument splitDocument : splitDocuments) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
splitDocumentsBoas.add(baos);
splitDocument.close();
}

document.close();

// After this line, you can find your zip logic integrated
Path zipFile = Files.createTempFile("split_documents", ".zip");
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
byte[] data;
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
// loop through the split documents and write them to the zip file
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf"; // You should replace "originalFileName" with the real file name
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();

// Add PDF file to the zip
ZipEntry pdfEntry = new ZipEntry(fileName);
zipOut.putNextEntry(pdfEntry);
zipOut.write(pdf);
zipOut.closeEntry();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
data = Files.readAllBytes(zipFile);
Files.delete(zipFile);
}



// return the Resource in the response
return WebResponseUtils.bytesToWebResponse(data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
}


private static String decodeQRCode(BufferedImage bufferedImage) {
LuminanceSource source;

if (bufferedImage.getRaster().getDataBuffer() instanceof DataBufferByte) {
byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
source = new PlanarYUVLuminanceSource(pixels, bufferedImage.getWidth(), bufferedImage.getHeight(), 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), false);
} else if (bufferedImage.getRaster().getDataBuffer() instanceof DataBufferInt) {
int[] pixels = ((DataBufferInt) bufferedImage.getRaster().getDataBuffer()).getData();
byte[] newPixels = new byte[pixels.length];
for (int i = 0; i < pixels.length; i++) {
newPixels[i] = (byte) (pixels[i] & 0xff);
}
source = new PlanarYUVLuminanceSource(newPixels, bufferedImage.getWidth(), bufferedImage.getHeight(), 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), false);
} else {
throw new IllegalArgumentException("BufferedImage must have 8-bit gray scale, 24-bit RGB, 32-bit ARGB (packed int), byte gray, or 3-byte/4-byte RGB image data");
}

BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

try {
Result result = new MultiFormatReader().decode(bitmap);
return result.getText();
} catch (NotFoundException e) {
return null; // there is no QR code in the image
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,12 @@ public String cropForm(Model model) {
model.addAttribute("currentPage", "crop");
return "crop";
}


@GetMapping("/auto-split-pdf")
@Hidden
public String autoSPlitPDFForm(Model model) {
model.addAttribute("currentPage", "auto-split-pdf");
return "auto-split-pdf";
}
}
3 changes: 3 additions & 0 deletions src/main/resources/messages_en_GB.properties
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ home.adjust-contrast.desc=Adjust Contrast, Saturation and Brightness of a PDF
home.crop.title=Crop PDF
home.crop.desc=Crop a PDF to reduce its size (maintains text!)

home.autoSplitPDF.title=Auto Split Pages
home.autoSplitPDF.desc=Auto Split Scanned PDF with physical scanned page splitter QR Code

error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect

downloadPdf=Download PDF
Expand Down
31 changes: 31 additions & 0 deletions src/main/resources/templates/auto-split-pdf.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!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=#{autoSplitPDF.title})}"></th:block>


<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{autoSplitPDF.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{auto-split-pdf}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{autoSplitPDF.submit}"></button>

</form>

</div>
</div>
</div>

</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>
2 changes: 2 additions & 0 deletions src/main/resources/templates/fragments/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'remove-pages', 'images/file-earmark-x.svg', 'home.removePages.title', 'home.removePages.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'multi-page-layout', 'images/page-layout.svg', 'home.pageLayout.title', 'home.pageLayout.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'scale-pages', 'images/scale-pages.svg', 'home.scalePages.title', 'home.scalePages.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'auto-split-pdf', 'images/layout-split.svg', 'home.autoSplitPDF.title', 'home.autoSplitPDF.desc')}"></div>


</div>
</li>
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ <h1 class="display-4" th:text="${@appName}"></h1>
<div th:replace="~{fragments/card :: card(id='auto-rename', cardTitle=#{home.auto-rename.title}, cardText=#{home.auto-rename.desc}, cardLink='auto-rename', svgPath='images/fonts.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='adjust-contrast', cardTitle=#{home.adjust-contrast.title}, cardText=#{home.adjust-contrast.desc}, cardLink='adjust-contrast', svgPath='images/adjust-contrast.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='crop', cardTitle=#{home.crop.title}, cardText=#{home.crop.desc}, cardLink='crop', svgPath='images/crop.svg')}"></div>

<div th:replace="~{fragments/card :: card(id='auto-split-pdf', cardTitle=#{home.autoSplitPDF.title}, cardText=#{home.autoSplitPDF.desc}, cardLink='auto-split-pdf', svgPath='images/layout-split.svg')}"></div>


</div>
Expand Down

0 comments on commit 6e32c7f

Please sign in to comment.