Skip to content

Commit

Permalink
Issue1632 remove images (#1645)
Browse files Browse the repository at this point in the history
* Implemented PdfImageRemovalService.java and PdfImageRemovalController.java. Image can be removed testing using Postman, but the file size doesn't change.

* Fix removal logic in service file to decrease file size.

* Implement "Remove Image" feature on the website

Updated the front-end code to integrate the "Remove Image" feature. The new functionality is now fully operational on the website, allowing users to remove images as expected.

* Add comments to PdfImageRemovalController and PdfImageRemovalService.

* Change the google material icon in navbar, homepage and remove-image-pdf.html.
  • Loading branch information
PingLin8888 authored Aug 8, 2024
1 parent bc35745 commit d0bf385
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public void init() {
addEndpointToGroup("Other", "auto-rename");
addEndpointToGroup("Other", "get-info-on-pdf");
addEndpointToGroup("Other", "show-javascript");
addEndpointToGroup("Other", "remove-image-pdf");

// CLI
addEndpointToGroup("CLI", "compress-pdf");
Expand Down Expand Up @@ -221,6 +222,7 @@ public void init() {
addEndpointToGroup("Java", "split-pdf-by-sections");
addEndpointToGroup("Java", REMOVE_BLANKS);
addEndpointToGroup("Java", "pdf-to-text");
addEndpointToGroup("Java", "remove-image-pdf");

// Javascript
addEndpointToGroup("Javascript", "pdf-organizer");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package stirling.software.SPDF.controller.api;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import io.swagger.v3.oas.annotations.Operation;

import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.service.PdfImageRemovalService;
import stirling.software.SPDF.utils.WebResponseUtils;


/**
* Controller class for handling PDF image removal requests.
* Provides an endpoint to remove images from a PDF file to reduce its size.
*/
@RestController
@RequestMapping("/api/v1/general")
public class PdfImageRemovalController {

// Service for removing images from PDFs
@Autowired private PdfImageRemovalService pdfImageRemovalService;

/**
* Constructor for dependency injection of PdfImageRemovalService.
*
* @param pdfImageRemovalService The service used for removing images from PDFs.
*/
public PdfImageRemovalController(PdfImageRemovalService pdfImageRemovalService) {
this.pdfImageRemovalService = pdfImageRemovalService;
}

/**
* Endpoint to remove images from a PDF file.
*
* This method processes the uploaded PDF file, removes all images, and returns
* the modified PDF file with a new name indicating that images were removed.
*
* @param file The PDF file with images to be removed.
* @return ResponseEntity containing the modified PDF file as byte array with appropriate content type and filename.
* @throws IOException If an error occurs while processing the PDF file.
*/
@PostMapping(consumes = "multipart/form-data", value = "/remove-image-pdf")
@Operation(
summary = "Remove images from file to reduce the file size.",
description =
"This endpoint remove images from file to reduce the file size.Input:PDF Output:PDF Type:MISO")
public ResponseEntity<byte[]> removeImages(@ModelAttribute PDFFile file) throws IOException {

MultipartFile pdf = file.getFileInput();

// Convert the MultipartFile to a byte array
byte[] pdfBytes = pdf.getBytes();

// Load the PDF document from the byte array
PDDocument document = Loader.loadPDF(pdfBytes);

// Remove images from the PDF document using the service
PDDocument modifiedDocument = pdfImageRemovalService.removeImagesFromPdf(document);

// Create a ByteArrayOutputStream to hold the modified PDF data
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

// Save the modified PDF document to the output stream
modifiedDocument.save(outputStream);
modifiedDocument.close();

// Generate a new filename for the modified PDF
String mergedFileName =
pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_images.pdf";

// Convert the byte array to a web response and return it
return WebResponseUtils.bytesToWebResponse(outputStream.toByteArray(), mergedFileName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,11 @@ public String autoSPlitPDFForm(Model model) {
model.addAttribute("currentPage", "auto-split-pdf");
return "auto-split-pdf";
}

@GetMapping("/remove-image-pdf")
@Hidden
public String removeImagePdfForm(Model model) {
model.addAttribute("currentPage", "remove-image-pdf");
return "remove-image-pdf";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package stirling.software.SPDF.service;

import java.io.IOException;

import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.springframework.stereotype.Service;

/**
* Service class responsible for removing image objects from a PDF document.
*/
@Service
public class PdfImageRemovalService {

/**
* Removes all image objects from the provided PDF document.
*
* This method iterates over each page in the document and removes any
* image XObjects found in the page's resources.
*
* @param document The PDF document from which images will be removed.
* @return The modified PDF document with images removed.
* @throws IOException If an error occurs while processing the PDF document.
*/
public PDDocument removeImagesFromPdf(PDDocument document) throws IOException {
// Iterate over each page in the PDF document
for (PDPage page : document.getPages()) {
PDResources resources = page.getResources();
// Iterate over all XObject names in the page's resources
for (COSName name : resources.getXObjectNames()) {
// Check if the XObject is an image
if (resources.isImageXObject(name)) {
// Remove the image XObject by setting it to null
resources.put(name, (PDXObject) null);
}
}
}
return document;
}
}
11 changes: 11 additions & 0 deletions src/main/resources/messages_en_GB.properties
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf

home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side


###########################
# #
Expand Down Expand Up @@ -1125,3 +1129,10 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post


#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
11 changes: 11 additions & 0 deletions src/main/resources/messages_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle

home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side


###########################
# #
Expand Down Expand Up @@ -1125,3 +1129,10 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post


#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image
22 changes: 22 additions & 0 deletions src/main/resources/static/css/removeImage.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.filename {
flex-grow: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 10px;
}

.arrows {
flex-shrink: 0;
display: flex;
justify-content: flex-end;
}
.arrows .btn {
margin: 0 3px;
}

.move-up span,
.move-down span {
font-weight: bold;
font-size: 1.2em;
}
3 changes: 3 additions & 0 deletions src/main/resources/templates/fragments/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ <h6 class="menu-title" th:text="#{navbar.sections.edit}"></h6>
<div
th:replace="~{fragments/navbarEntry :: navbarEntry ('get-info-on-pdf', 'info', 'home.getPdfInfo.title', 'home.getPdfInfo.desc', 'getPdfInfo.tags', 'other')}">
</div>
<div
th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-image-pdf','remove_selection', 'home.removeImagePdf.title', 'home.removeImagePdf.desc', 'removeImagePdf.tags', 'other')}">
</div>
</div>
<!-- Advance menu items -->
<div class="col-lg-2 col-sm-6 py px-xl-1 px-2">
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ <h5 class="card-title" th:text="#{settings.update}"></h5>
<div
th:replace="~{fragments/card :: card(id='stamp', cardTitle=#{home.AddStampRequest.title}, cardText=#{home.AddStampRequest.desc}, cardLink='stamp', toolIcon='approval', tags=#{AddStampRequest.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='remove-image-pdf', cardTitle=#{home.removeImagePdf.title}, cardText=#{home.removeImagePdf.desc}, cardLink='remove-image-pdf', toolIcon='remove_selection', tags=#{removeImagePdf.tags}, toolGroup='other')}">
</div>
</div>
</div>
</div>
Expand Down
36 changes: 36 additions & 0 deletions src/main/resources/templates/remove-image-pdf.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block
th:insert="~{fragments/common :: head(title=#{removeImage.title}, header=#{removeImage.header})}"></th:block>
<link rel="stylesheet" th:href="@{'/css/removeImage.css'}">
</head>

<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon word">remove_selection</span>
<span class="tool-header-text" th:text="#{removeImage.header}"></span>
</div>
<form action="api/v1/general/remove-image-pdf" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>

<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeImage.submit}"></button>
</form>
</div>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

0 comments on commit d0bf385

Please sign in to comment.