mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01:00 
			
		
		
		
	show javascript, bug fixes
This commit is contained in:
		
							parent
							
								
									379791a326
								
							
						
					
					
						commit
						54f53be5b5
					
				@ -8,7 +8,7 @@ plugins {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
group = 'stirling.software'
 | 
			
		||||
version = '0.12.0'
 | 
			
		||||
version = '0.12.1'
 | 
			
		||||
sourceCompatibility = '17'
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,141 @@
 | 
			
		||||
package stirling.software.SPDF.controller.api.other;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
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 io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
import stirling.software.SPDF.utils.GeneralUtils;
 | 
			
		||||
import stirling.software.SPDF.utils.PdfUtils;
 | 
			
		||||
import stirling.software.SPDF.utils.WebResponseUtils;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.*;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.common.*;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.PDPageContentStream.*;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
import org.springframework.http.*;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
import io.swagger.v3.oas.annotations.*;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.*;
 | 
			
		||||
import io.swagger.v3.oas.annotations.parameters.*;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
 | 
			
		||||
import org.apache.pdfbox.text.TextPosition;
 | 
			
		||||
import org.apache.tomcat.util.http.ResponseUtil;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.URLEncoder;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 | 
			
		||||
import org.springframework.http.ResponseEntity;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestParam;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ArrayNode;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import com.itextpdf.io.font.constants.StandardFonts;
 | 
			
		||||
import com.itextpdf.kernel.font.PdfFont;
 | 
			
		||||
import com.itextpdf.kernel.font.PdfFontFactory;
 | 
			
		||||
import com.itextpdf.kernel.geom.Rectangle;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfReader;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfStream;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfWriter;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfArray;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfDictionary;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfDocument;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfName;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfObject;
 | 
			
		||||
import com.itextpdf.kernel.pdf.PdfPage;
 | 
			
		||||
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
 | 
			
		||||
import com.itextpdf.layout.Canvas;
 | 
			
		||||
import com.itextpdf.layout.element.Paragraph;
 | 
			
		||||
import com.itextpdf.layout.properties.TextAlignment;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.*;
 | 
			
		||||
import org.apache.pdfbox.text.*;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
import io.swagger.v3.oas.annotations.*;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import org.springframework.http.ResponseEntity;
 | 
			
		||||
@RestController
 | 
			
		||||
@Tag(name = "Other", description = "Other APIs")
 | 
			
		||||
public class ShowJavascript {
 | 
			
		||||
 | 
			
		||||
    private static final Logger logger = LoggerFactory.getLogger(ShowJavascript.class);
 | 
			
		||||
    @PostMapping(consumes = "multipart/form-data", value = "/show-javascript")
 | 
			
		||||
    @Operation(summary = "Extract header from PDF file", description = "This endpoint accepts a PDF file and attempts to extract its title or header based on heuristics. Input:PDF Output:PDF Type:SISO")
 | 
			
		||||
    public ResponseEntity<byte[]> extractHeader(
 | 
			
		||||
    	            @RequestPart(value = "fileInput") @Parameter(description = "The input PDF file from which the javascript is to be extracted.", required = true) MultipartFile inputFile)  
 | 
			
		||||
    	            throws Exception {
 | 
			
		||||
 | 
			
		||||
    	try (
 | 
			
		||||
			    PdfDocument itextDoc = new PdfDocument(new PdfReader(inputFile.getInputStream()))
 | 
			
		||||
			) {
 | 
			
		||||
    	        
 | 
			
		||||
    		String name = "";
 | 
			
		||||
    		String script = "";
 | 
			
		||||
    		String entryName = "File: "+inputFile.getOriginalFilename() + ",  Script: ";
 | 
			
		||||
    	      //Javascript
 | 
			
		||||
                PdfDictionary namesDict = itextDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Names);
 | 
			
		||||
                if (namesDict != null) {
 | 
			
		||||
                    PdfDictionary javascriptDict = namesDict.getAsDictionary(PdfName.JavaScript);
 | 
			
		||||
                    if (javascriptDict != null) {
 | 
			
		||||
 | 
			
		||||
                        PdfArray namesArray = javascriptDict.getAsArray(PdfName.Names);
 | 
			
		||||
                        for (int i = 0; i < namesArray.size(); i += 2) {
 | 
			
		||||
                            if(namesArray.getAsString(i) != null)
 | 
			
		||||
                            	name =  namesArray.getAsString(i).toString();
 | 
			
		||||
 | 
			
		||||
                            PdfObject jsCode = namesArray.get(i+1);
 | 
			
		||||
                            if (jsCode instanceof PdfStream) {
 | 
			
		||||
                                byte[] jsCodeBytes = ((PdfStream)jsCode).getBytes();
 | 
			
		||||
                                String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
 | 
			
		||||
                                script = "//" + entryName + name + "\n" +jsCodeStr;
 | 
			
		||||
 | 
			
		||||
                            } else if (jsCode instanceof PdfDictionary) {
 | 
			
		||||
                                // If the JS code is in a dictionary, you'll need to know the key to use.
 | 
			
		||||
                                // Assuming the key is PdfName.JS:
 | 
			
		||||
                                PdfStream jsCodeStream = ((PdfDictionary)jsCode).getAsStream(PdfName.JS);
 | 
			
		||||
                                if (jsCodeStream != null) {
 | 
			
		||||
                                    byte[] jsCodeBytes = jsCodeStream.getBytes();
 | 
			
		||||
                                    String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
 | 
			
		||||
                                    script = "//" + entryName + name + "\n" +jsCodeStr;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if(script.equals("")) {
 | 
			
		||||
                	script = "PDF '" +inputFile.getOriginalFilename() + "' does not contain Javascript";
 | 
			
		||||
                }
 | 
			
		||||
               return WebResponseUtils.bytesToWebResponse(script.getBytes(), name + ".js");
 | 
			
		||||
    	}
 | 
			
		||||
    	
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -43,6 +43,7 @@ import com.itextpdf.kernel.pdf.layer.PdfOCProperties;
 | 
			
		||||
import com.itextpdf.kernel.xmp.XMPException;
 | 
			
		||||
import com.itextpdf.kernel.xmp.XMPMeta;
 | 
			
		||||
import com.itextpdf.kernel.xmp.XMPMetaFactory;
 | 
			
		||||
import com.itextpdf.kernel.xmp.options.SerializeOptions;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
@ -193,11 +194,13 @@ public class GetInfoOnPDF {
 | 
			
		||||
            if (embeddedFiles != null) {
 | 
			
		||||
                
 | 
			
		||||
                PdfArray namesArray = embeddedFiles.getAsArray(PdfName.Names);
 | 
			
		||||
                for (int i = 0; i < namesArray.size(); i += 2) {
 | 
			
		||||
                    ObjectNode embeddedFileNode = objectMapper.createObjectNode();
 | 
			
		||||
                    embeddedFileNode.put("Name", namesArray.getAsString(i).toString());
 | 
			
		||||
                    // Add other details if required
 | 
			
		||||
                    embeddedFilesArray.add(embeddedFileNode);
 | 
			
		||||
                if(namesArray != null) {
 | 
			
		||||
	                for (int i = 0; i < namesArray.size(); i += 2) {
 | 
			
		||||
	                    ObjectNode embeddedFileNode = objectMapper.createObjectNode();
 | 
			
		||||
	                    embeddedFileNode.put("Name", namesArray.getAsString(i).toString());
 | 
			
		||||
	                    // Add other details if required
 | 
			
		||||
	                    embeddedFilesArray.add(embeddedFileNode);
 | 
			
		||||
	                }
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
            }
 | 
			
		||||
@ -228,8 +231,26 @@ public class GetInfoOnPDF {
 | 
			
		||||
                    PdfArray namesArray = javascriptDict.getAsArray(PdfName.Names);
 | 
			
		||||
                    for (int i = 0; i < namesArray.size(); i += 2) {
 | 
			
		||||
                        ObjectNode jsNode = objectMapper.createObjectNode();
 | 
			
		||||
                        jsNode.put("JS Name", namesArray.getAsString(i).toString());
 | 
			
		||||
                        jsNode.put("JS Code", namesArray.getAsString(i + 1).toString());
 | 
			
		||||
                        if(namesArray.getAsString(i) != null)
 | 
			
		||||
                            jsNode.put("JS Name", namesArray.getAsString(i).toString());
 | 
			
		||||
 | 
			
		||||
                        // Here we check for a PdfStream object and retrieve the JS code from it
 | 
			
		||||
                        PdfObject jsCode = namesArray.get(i+1);
 | 
			
		||||
                        if (jsCode instanceof PdfStream) {
 | 
			
		||||
                            byte[] jsCodeBytes = ((PdfStream)jsCode).getBytes();
 | 
			
		||||
                            String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
 | 
			
		||||
                            jsNode.put("JS Script Length", jsCodeStr.length());
 | 
			
		||||
                        } else if (jsCode instanceof PdfDictionary) {
 | 
			
		||||
                            // If the JS code is in a dictionary, you'll need to know the key to use.
 | 
			
		||||
                            // Assuming the key is PdfName.JS:
 | 
			
		||||
                            PdfStream jsCodeStream = ((PdfDictionary)jsCode).getAsStream(PdfName.JS);
 | 
			
		||||
                            if (jsCodeStream != null) {
 | 
			
		||||
                                byte[] jsCodeBytes = jsCodeStream.getBytes();
 | 
			
		||||
                                String jsCodeStr = new String(jsCodeBytes, StandardCharsets.UTF_8);
 | 
			
		||||
                                jsNode.put("JS Script Character Length", jsCodeStr.length());
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        javascriptArray.add(jsNode);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -305,16 +326,15 @@ public class GetInfoOnPDF {
 | 
			
		||||
            }
 | 
			
		||||
            other.set("Bookmarks/Outline/TOC", bookmarksArray);
 | 
			
		||||
            
 | 
			
		||||
            byte[] xmpBytes = itextDoc.getXmpMetadata();
 | 
			
		||||
            String xmpString = null;
 | 
			
		||||
            try {
 | 
			
		||||
                byte[] xmpBytes = itextDoc.getXmpMetadata();
 | 
			
		||||
                if (xmpBytes != null) {
 | 
			
		||||
            if (xmpBytes != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(xmpBytes);
 | 
			
		||||
                    xmpString = xmpMeta.dumpObject();
 | 
			
		||||
                    
 | 
			
		||||
                    xmpString = new String(XMPMetaFactory.serializeToBuffer(xmpMeta, new SerializeOptions()));
 | 
			
		||||
                } catch (XMPException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            } catch (XMPException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
            }
 | 
			
		||||
            other.put("XMPMetadata", xmpString);
 | 
			
		||||
            
 | 
			
		||||
@ -416,8 +436,10 @@ public class GetInfoOnPDF {
 | 
			
		||||
                for (PdfAnnotation annotation : annotations) {
 | 
			
		||||
                    if (annotation instanceof PdfLinkAnnotation) {
 | 
			
		||||
                        PdfLinkAnnotation linkAnnotation = (PdfLinkAnnotation) annotation;
 | 
			
		||||
                        String uri = linkAnnotation.getAction().toString();
 | 
			
		||||
                        uniqueURIs.add(uri);  // Add to set to ensure uniqueness
 | 
			
		||||
                        if(linkAnnotation != null && linkAnnotation.getAction() != null) {
 | 
			
		||||
	                        String uri = linkAnnotation.getAction().toString();
 | 
			
		||||
	                        uniqueURIs.add(uri);  // Add to set to ensure uniqueness
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package stirling.software.SPDF.controller.api.security;
 | 
			
		||||
import org.apache.pdfbox.cos.COSName;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.PDDocument;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.PDPage;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.PDResources;
 | 
			
		||||
import org.apache.pdfbox.pdmodel.PDPageTree;
 | 
			
		||||
@ -21,7 +22,9 @@ import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import stirling.software.SPDF.utils.WebResponseUtils;
 | 
			
		||||
 | 
			
		||||
import org.apache.pdfbox.cos.COSDictionary;
 | 
			
		||||
import org.apache.pdfbox.cos.COSName;
 | 
			
		||||
import org.apache.pdfbox.cos.COSString;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
@ -75,8 +78,24 @@ public class SanitizeController {
 | 
			
		||||
	        return WebResponseUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_sanitized.pdf");
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
    private void sanitizeJavaScript(PDDocument document) throws IOException {
 | 
			
		||||
    	for (PDPage page : document.getPages()) {
 | 
			
		||||
	private void sanitizeJavaScript(PDDocument document) throws IOException {
 | 
			
		||||
		// Get the root dictionary (catalog) of the PDF
 | 
			
		||||
	    PDDocumentCatalog catalog = document.getDocumentCatalog();
 | 
			
		||||
 | 
			
		||||
	    // Get the Names dictionary
 | 
			
		||||
	    COSDictionary namesDict = (COSDictionary) catalog.getCOSObject().getDictionaryObject(COSName.NAMES);
 | 
			
		||||
 | 
			
		||||
	    if (namesDict != null) {
 | 
			
		||||
	        // Get the JavaScript dictionary
 | 
			
		||||
	        COSDictionary javaScriptDict = (COSDictionary) namesDict.getDictionaryObject(COSName.getPDFName("JavaScript"));
 | 
			
		||||
 | 
			
		||||
	        if (javaScriptDict != null) {
 | 
			
		||||
	            // Remove the JavaScript dictionary
 | 
			
		||||
	            namesDict.removeItem(COSName.getPDFName("JavaScript"));
 | 
			
		||||
	        }
 | 
			
		||||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    for (PDPage page : document.getPages()) {
 | 
			
		||||
            for (PDAnnotation annotation : page.getAnnotations()) {
 | 
			
		||||
                if (annotation instanceof PDAnnotationWidget) {
 | 
			
		||||
                    PDAnnotationWidget widget = (PDAnnotationWidget) annotation;
 | 
			
		||||
@ -89,13 +108,28 @@ public class SanitizeController {
 | 
			
		||||
	        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
 | 
			
		||||
	        if (acroForm != null) {
 | 
			
		||||
	            for (PDField field : acroForm.getFields()) {
 | 
			
		||||
	                if (field.getActions().getF() instanceof PDActionJavaScript) {
 | 
			
		||||
	                    field.getActions().setF(null);
 | 
			
		||||
	                }
 | 
			
		||||
	            	PDFormFieldAdditionalActions actions = field.getActions();
 | 
			
		||||
	            	if(actions != null) {
 | 
			
		||||
	            		if (actions.getC() instanceof PDActionJavaScript) {
 | 
			
		||||
	                        actions.setC(null);
 | 
			
		||||
	                    }
 | 
			
		||||
	                    if (actions.getF() instanceof PDActionJavaScript) {
 | 
			
		||||
	                        actions.setF(null);
 | 
			
		||||
	                    }
 | 
			
		||||
	                    if (actions.getK() instanceof PDActionJavaScript) {
 | 
			
		||||
	                        actions.setK(null);
 | 
			
		||||
	                    }
 | 
			
		||||
	                    if (actions.getV() instanceof PDActionJavaScript) {
 | 
			
		||||
	                        actions.setV(null);
 | 
			
		||||
	                    }
 | 
			
		||||
	            	}
 | 
			
		||||
	            }
 | 
			
		||||
	        }
 | 
			
		||||
	    }
 | 
			
		||||
    }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private void sanitizeEmbeddedFiles(PDDocument document) {
 | 
			
		||||
        PDPageTree allPages = document.getPages();
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,14 @@ public class OtherWebController {
 | 
			
		||||
        return modelAndView;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @GetMapping("/show-javascript")
 | 
			
		||||
    @Hidden
 | 
			
		||||
    public String extractJavascriptForm(Model model) {
 | 
			
		||||
        model.addAttribute("currentPage", "show-javascript");
 | 
			
		||||
        return "other/show-javascript";
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    @GetMapping("/add-page-numbers")
 | 
			
		||||
    @Hidden
 | 
			
		||||
    public String addPageNumbersForm(Model model) {
 | 
			
		||||
 | 
			
		||||
@ -255,12 +255,21 @@ home.PdfToSinglePage.title=PDF to Single Large Page
 | 
			
		||||
home.PdfToSinglePage.desc=Merges all PDF pages into one large single page
 | 
			
		||||
PdfToSinglePage.tags=single page
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
home.showJS.title=Show Javascript
 | 
			
		||||
home.showJS.desc=Searches and displays any JS injected into a PDF
 | 
			
		||||
showJS.tags=JS
 | 
			
		||||
 | 
			
		||||
###########################
 | 
			
		||||
#                         #
 | 
			
		||||
#       WEB PAGES         #
 | 
			
		||||
#                         #
 | 
			
		||||
###########################
 | 
			
		||||
 | 
			
		||||
#showJS
 | 
			
		||||
showJS.title=Show Javascript
 | 
			
		||||
showJS.header=Show Javascript
 | 
			
		||||
showJS.downloadJS=Download Javascript
 | 
			
		||||
showJS.submit=Show
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#pdfToSinglePage
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								src/main/resources/static/css/prism.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/main/resources/static/css/prism.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
/* PrismJS 1.29.0
 | 
			
		||||
https://prismjs.com/download.html#themes=prism-coy&languages=clike+javascript */
 | 
			
		||||
code[class*=language-],pre[class*=language-]{color:#000;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{position:relative;margin:.5em 0;overflow:visible;padding:1px}pre[class*=language-]>code{position:relative;z-index:1;border-left:10px solid #358ccb;box-shadow:-1px 0 0 0 #358ccb,0 0 0 1px #dfdfdf;background-color:#fdfdfd;background-image:linear-gradient(transparent 50%,rgba(69,142,209,.04) 50%);background-size:3em 3em;background-origin:content-box;background-attachment:local}code[class*=language-]{max-height:inherit;height:inherit;padding:0 1em;display:block;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background-color:#fdfdfd;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin-bottom:1em}:not(pre)>code[class*=language-]{position:relative;padding:.2em;border-radius:.3em;color:#c92c2c;border:1px solid rgba(0,0,0,.1);display:inline;white-space:normal}pre[class*=language-]:after,pre[class*=language-]:before{content:'';display:block;position:absolute;bottom:.75em;left:.18em;width:40%;height:20%;max-height:13em;box-shadow:0 13px 8px #979797;-webkit-transform:rotate(-2deg);-moz-transform:rotate(-2deg);-ms-transform:rotate(-2deg);-o-transform:rotate(-2deg);transform:rotate(-2deg)}pre[class*=language-]:after{right:.75em;left:auto;-webkit-transform:rotate(2deg);-moz-transform:rotate(2deg);-ms-transform:rotate(2deg);-o-transform:rotate(2deg);transform:rotate(2deg)}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#7d8b99}.token.punctuation{color:#5f6364}.token.boolean,.token.constant,.token.deleted,.token.function-name,.token.number,.token.property,.token.symbol,.token.tag{color:#c92c2c}.token.attr-name,.token.builtin,.token.char,.token.function,.token.inserted,.token.selector,.token.string{color:#2f9c0a}.token.entity,.token.operator,.token.url,.token.variable{color:#a67f59;background:rgba(255,255,255,.5)}.token.atrule,.token.attr-value,.token.class-name,.token.keyword{color:#1990b8}.token.important,.token.regex{color:#e90}.language-css .token.string,.style .token.string{color:#a67f59;background:rgba(255,255,255,.5)}.token.important{font-weight:400}.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.namespace{opacity:.7}@media screen and (max-width:767px){pre[class*=language-]:after,pre[class*=language-]:before{bottom:14px;box-shadow:none}}pre[class*=language-].line-numbers.line-numbers{padding-left:0}pre[class*=language-].line-numbers.line-numbers code{padding-left:3.8em}pre[class*=language-].line-numbers.line-numbers .line-numbers-rows{left:0}pre[class*=language-][data-line]{padding-top:0;padding-bottom:0;padding-left:0}pre[data-line] code{position:relative;padding-left:4em}pre .line-highlight{margin-top:0}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/main/resources/static/images/js.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/main/resources/static/images/js.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filetype-js" viewBox="0 0 16 16">
 | 
			
		||||
  <path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2H8v-1h4a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5ZM3.186 15.29a1.176 1.176 0 0 1-.111-.449h.765a.578.578 0 0 0 .255.384c.07.049.153.087.249.114.095.028.202.041.319.041.164 0 .302-.023.413-.07a.559.559 0 0 0 .255-.193.507.507 0 0 0 .085-.29.387.387 0 0 0-.153-.326c-.101-.08-.255-.144-.462-.193l-.619-.143a1.72 1.72 0 0 1-.539-.214 1.001 1.001 0 0 1-.351-.367 1.068 1.068 0 0 1-.123-.524c0-.244.063-.457.19-.639.127-.181.303-.322.528-.422.224-.1.483-.149.776-.149.305 0 .564.05.78.152.216.102.383.239.5.41.12.17.186.359.2.566h-.75a.56.56 0 0 0-.12-.258.624.624 0 0 0-.247-.181.923.923 0 0 0-.369-.068c-.217 0-.388.05-.513.152a.472.472 0 0 0-.184.384c0 .121.048.22.143.3a.97.97 0 0 0 .405.175l.62.143c.218.05.406.12.566.211.16.09.285.21.375.358.09.148.135.335.135.56 0 .247-.063.466-.188.656a1.216 1.216 0 0 1-.539.439c-.234.105-.52.158-.858.158-.254 0-.476-.03-.665-.09a1.404 1.404 0 0 1-.478-.252 1.13 1.13 0 0 1-.29-.375Zm-3.104-.033A1.32 1.32 0 0 1 0 14.791h.765a.576.576 0 0 0 .073.27.499.499 0 0 0 .454.246c.19 0 .33-.055.422-.164.092-.11.138-.265.138-.466v-2.745h.79v2.725c0 .44-.119.774-.357 1.005-.236.23-.564.345-.984.345a1.59 1.59 0 0 1-.569-.094 1.145 1.145 0 0 1-.407-.266 1.14 1.14 0 0 1-.243-.39Z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
@ -80,9 +80,13 @@ function setupFileInput(chooser) {
 | 
			
		||||
    document.body.addEventListener('dragleave', dragleaveListener);
 | 
			
		||||
    document.body.addEventListener('drop', dropListener);
 | 
			
		||||
 | 
			
		||||
    $("#" + elementId).on("change", function() {
 | 
			
		||||
        handleFileInputChange(this);
 | 
			
		||||
    });
 | 
			
		||||
    console.log("Element Id: ", elementId);
 | 
			
		||||
 | 
			
		||||
	$("#" + elementId).on("change", function(e) {
 | 
			
		||||
	    allFiles = Array.from(e.target.files); 
 | 
			
		||||
	    handleFileInputChange(this);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function handleFileInputChange(inputElement) {
 | 
			
		||||
        const files = allFiles;
 | 
			
		||||
@ -92,6 +96,7 @@ function setupFileInput(chooser) {
 | 
			
		||||
        fileNames.forEach(fileName => {
 | 
			
		||||
            selectedFilesContainer.append("<div>" + fileName + "</div>");
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        if (fileNames.length === 1) {
 | 
			
		||||
            $(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
 | 
			
		||||
        } else if (fileNames.length > 1) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								src/main/resources/static/js/thirdParty/prism.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/main/resources/static/js/thirdParty/prism.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -109,7 +109,7 @@
 | 
			
		||||
                </li>
 | 
			
		||||
 | 
			
		||||
                  <li class="nav-item nav-item-separator"></li> 
 | 
			
		||||
                    <li class="nav-item dropdown" th:classappend="${currentPage}=='sign' OR ${currentPage}=='repair' OR ${currentPage}=='compare' OR ${currentPage}=='flatten' OR ${currentPage}=='remove-blanks' OR ${currentPage}=='extract-image-scans' OR ${currentPage}=='change-metadata' OR ${currentPage}=='add-image' OR ${currentPage}=='ocr-pdf' OR ${currentPage}=='change-permissions' OR ${currentPage}=='extract-images' OR ${currentPage}=='compress-pdf' OR ${currentPage}=='add-page-numbers' OR ${currentPage}=='auto-rename' OR ${currentPage}=='get-info-on-pdf' ? 'active' : ''">
 | 
			
		||||
                    <li class="nav-item dropdown" th:classappend="${currentPage}=='sign' OR ${currentPage}=='repair' OR ${currentPage}=='compare' OR ${currentPage}=='show-javascript' OR ${currentPage}=='flatten' OR ${currentPage}=='remove-blanks' OR ${currentPage}=='extract-image-scans' OR ${currentPage}=='change-metadata' OR ${currentPage}=='add-image' OR ${currentPage}=='ocr-pdf' OR ${currentPage}=='change-permissions' OR ${currentPage}=='extract-images' OR ${currentPage}=='compress-pdf' OR ${currentPage}=='add-page-numbers' OR ${currentPage}=='auto-rename' OR ${currentPage}=='get-info-on-pdf' ? 'active' : ''">
 | 
			
		||||
                        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
 | 
			
		||||
                        <img class="icon" src="images/card-list.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> 
 | 
			
		||||
                        <span class="icon-text"  th:text="#{navbar.other}"></span>
 | 
			
		||||
@ -131,6 +131,7 @@
 | 
			
		||||
                            <div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-page-numbers', 'images/add-page-numbers.svg', 'home.add-page-numbers.title', 'home.add-page-numbers.desc', 'add-page-numbers.tags')}"></div>
 | 
			
		||||
                            <div th:replace="~{fragments/navbarEntry :: navbarEntry ('auto-rename', 'images/fonts.svg', 'home.auto-rename.title', 'home.auto-rename.desc', 'auto-rename.tags')}"></div>
 | 
			
		||||
                        	<div th:replace="~{fragments/navbarEntry :: navbarEntry ('get-info-on-pdf', 'images/info.svg', 'home.getPdfInfo.title', 'home.getPdfInfo.desc', 'getPdfInfo.tags')}"></div>
 | 
			
		||||
                        	<div th:replace="~{fragments/navbarEntry :: navbarEntry ('show-javascript', 'images/js.svg', 'home.showJS.title', 'home.showJS.desc', 'showJS.tags')}"></div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </li>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -88,6 +88,7 @@
 | 
			
		||||
                <div th:replace="~{fragments/card :: card(id='get-info-on-pdf', cardTitle=#{home.getPdfInfo.title}, cardText=#{home.getPdfInfo.desc}, cardLink='get-info-on-pdf', svgPath='images/info.svg')}"></div>
 | 
			
		||||
                <div th:replace="~{fragments/card :: card(id='extract-page', cardTitle=#{home.extractPage.title}, cardText=#{home.extractPage.desc}, cardLink='extract-page', svgPath='images/extract.svg')}"></div>
 | 
			
		||||
                <div th:replace="~{fragments/card :: card(id='pdf-to-single-page', cardTitle=#{home.PdfToSinglePage.title}, cardText=#{home.PdfToSinglePage.desc}, cardLink='pdf-to-single-page', svgPath='images/single-page.svg')}"></div>
 | 
			
		||||
                <div th:replace="~{fragments/card :: card(id='show-javascript', cardTitle=#{home.showJS.title}, cardText=#{home.showJS.desc}, cardLink='show-javascript', svgPath='images/js.svg')}"></div>
 | 
			
		||||
                
 | 
			
		||||
                
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										89
									
								
								src/main/resources/templates/other/show-javascript.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/main/resources/templates/other/show-javascript.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
<!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=#{showJS.title})}"></th:block>
 | 
			
		||||
<body>
 | 
			
		||||
	<link href="css/prism.css" rel="stylesheet" />
 | 
			
		||||
	<script src="js/thirdParty/prism.js"></script>
 | 
			
		||||
	<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-12">
 | 
			
		||||
						<h2 th:text="#{showJS.header}"></h2>
 | 
			
		||||
						<form id="pdfInfoForm" method="post" enctype="multipart/form-data"
 | 
			
		||||
							th:action="@{show-javascript}">
 | 
			
		||||
							<div
 | 
			
		||||
								th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, remoteCall='false')}"></div>
 | 
			
		||||
							<br>
 | 
			
		||||
							<button type="submit" id="submitBtn" class="btn btn-primary"
 | 
			
		||||
								th:text="#{showJS.submit}"></button>
 | 
			
		||||
 | 
			
		||||
						</form>
 | 
			
		||||
						<div class="container mt-5">
 | 
			
		||||
							<!-- Iterate over each main section in the JSON -->
 | 
			
		||||
							<div id="script-content">
 | 
			
		||||
								<!-- JavaScript will populate this section -->
 | 
			
		||||
							</div>
 | 
			
		||||
 | 
			
		||||
							<!-- Button to download the JSON -->
 | 
			
		||||
							<a href="#" id="downloadJS" class="btn btn-primary mt-3"
 | 
			
		||||
								style="display: none;" th:text="#{showJS.downloadJS}">Download
 | 
			
		||||
								JSON</a>
 | 
			
		||||
						</div>
 | 
			
		||||
<style>
 | 
			
		||||
    /* Add a max-height and make it scrollable */
 | 
			
		||||
    #script-content {
 | 
			
		||||
        max-height: 1000px; /* Adjust this to your preferred maximum height */
 | 
			
		||||
        overflow-y: auto;
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
						<script>
 | 
			
		||||
    document.querySelector('#pdfInfoForm').addEventListener('submit', function(event){
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
 | 
			
		||||
        // Fetch the formData
 | 
			
		||||
        const formData = new FormData(event.target);
 | 
			
		||||
 | 
			
		||||
        fetch('show-javascript', {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            body: formData
 | 
			
		||||
        }).then(response => response.text())
 | 
			
		||||
        .then(data => {
 | 
			
		||||
            // Escape < and > characters
 | 
			
		||||
            let escapedData = data.replace(/</g, '<').replace(/>/g, '>');
 | 
			
		||||
 | 
			
		||||
            // Wrap the JavaScript content in a pre and code tag and add it to the div
 | 
			
		||||
            document.querySelector('#script-content').innerHTML = '<pre><code class="language-javascript">' + escapedData + '</code></pre>';
 | 
			
		||||
 | 
			
		||||
            // Highlight the code using Prism.js
 | 
			
		||||
            Prism.highlightAll();
 | 
			
		||||
 | 
			
		||||
            // Create a blob object from the data and create a URL for it
 | 
			
		||||
            let blob = new Blob([data], {type: 'application/javascript'});
 | 
			
		||||
            let url = URL.createObjectURL(blob);
 | 
			
		||||
 | 
			
		||||
            // Set the URL as the href of the download button and provide a download name
 | 
			
		||||
            let downloadButton = document.querySelector('#downloadJS');
 | 
			
		||||
            downloadButton.href = url;
 | 
			
		||||
            downloadButton.download = 'extracted.js';
 | 
			
		||||
            downloadButton.style.display = 'block';
 | 
			
		||||
        })
 | 
			
		||||
        .catch(error => {
 | 
			
		||||
            console.error('Error:', error);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
		</div>
 | 
			
		||||
		<div th:insert="~{fragments/footer.html :: footer}"></div>
 | 
			
		||||
	</div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@ -73,46 +73,45 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                       function renderJsonSection(key, value, depth = 0) {
 | 
			
		||||
                    	    // Replace spaces and other non-alphanumeric characters with underscores for valid IDs
 | 
			
		||||
                    	    let safeKey = (typeof key === "string") ? key.replace(/[^a-zA-Z0-9]/g, '_') : key;
 | 
			
		||||
 | 
			
		||||
                    	    let output = `<div class="card mb-3">
 | 
			
		||||
                    	        <div class="card-header" id="${safeKey}-heading-${depth}">
 | 
			
		||||
                    	            <h5 class="mb-0">`;
 | 
			
		||||
 | 
			
		||||
                    	         // Check if the value is an object and has children
 | 
			
		||||
                    	            if (value && typeof value === 'object') {
 | 
			
		||||
                    	                // For arrays and non-array objects
 | 
			
		||||
                    	                if (Array.isArray(value) && value.length === 0) {
 | 
			
		||||
                    	                    output += `${key}: Empty array`;
 | 
			
		||||
                    	                } else if (!Array.isArray(value) && Object.keys(value).length === 0) {
 | 
			
		||||
                    	                    output += `${key}: Empty object`;
 | 
			
		||||
                    	                } else {
 | 
			
		||||
                    	                    output += `
 | 
			
		||||
                    	                        <button class="btn btn-link" type="button" data-toggle="collapse" data-target="#${safeKey}-content-${depth}" aria-expanded="true" aria-controls="${safeKey}-content-${depth}">
 | 
			
		||||
                    	                            ${key}
 | 
			
		||||
                    	                        </button>`;
 | 
			
		||||
                    	                }
 | 
			
		||||
                    	            } else {
 | 
			
		||||
                    	                // For simple key-value pairs
 | 
			
		||||
                    	                output += `${key}: ${value}`;
 | 
			
		||||
                    	            }
 | 
			
		||||
 | 
			
		||||
                    	    if (key === 'XMPMetadata' && typeof value === "string") {
 | 
			
		||||
                    	        output += `<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#${safeKey}-content-${depth}" aria-expanded="true" aria-controls="${safeKey}-content-${depth}">
 | 
			
		||||
                    	            ${key}
 | 
			
		||||
                    	        </button>`;
 | 
			
		||||
                    	    } else if (value && typeof value === 'object') {
 | 
			
		||||
                    	        if (Array.isArray(value) && value.length === 0) {
 | 
			
		||||
                    	            output += `${key}: Empty array`;
 | 
			
		||||
                    	        } else if (!Array.isArray(value) && Object.keys(value).length === 0) {
 | 
			
		||||
                    	            output += `${key}: Empty object`;
 | 
			
		||||
                    	        } else {
 | 
			
		||||
                    	            output += `<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#${safeKey}-content-${depth}" aria-expanded="true" aria-controls="${safeKey}-content-${depth}">
 | 
			
		||||
                    	                ${key}
 | 
			
		||||
                    	            </button>`;
 | 
			
		||||
                    	        }
 | 
			
		||||
                    	    } else {
 | 
			
		||||
                    	        output += `${key}: ${value}`;
 | 
			
		||||
                    	    }
 | 
			
		||||
 | 
			
		||||
                    	    output += `
 | 
			
		||||
                    	        </h5>
 | 
			
		||||
                    	    </div>
 | 
			
		||||
                    	    <div id="${safeKey}-content-${depth}" class="collapse" aria-labelledby="${safeKey}-heading-${depth}">`;
 | 
			
		||||
                    	            </h5>
 | 
			
		||||
                    	        </div>
 | 
			
		||||
                    	        <div id="${safeKey}-content-${depth}" class="collapse" aria-labelledby="${safeKey}-heading-${depth}">`;
 | 
			
		||||
 | 
			
		||||
                    	    // Check if the value is a nested object
 | 
			
		||||
                    	    if (value && typeof value === 'object' && !Array.isArray(value)) {
 | 
			
		||||
                    	    if (key === 'XMPMetadata' && typeof value === "string") {
 | 
			
		||||
                    	        output += `<div class="card-body"><pre>${escapeHTML(value)}</pre></div>`;
 | 
			
		||||
                    	    } else if (value && typeof value === 'object' && !Array.isArray(value)) {
 | 
			
		||||
                    	        output += '<div class="card-body">';
 | 
			
		||||
                    	        if (Object.keys(value).length) {
 | 
			
		||||
                    	            for (const subKey in value) {
 | 
			
		||||
                    	                output += renderJsonSection(subKey, value[subKey], depth + 1);
 | 
			
		||||
                    	            }
 | 
			
		||||
                    	        } else {
 | 
			
		||||
                    	            output += '<p class="text-muted">Empty object</p>';
 | 
			
		||||
                    	            output += '<p class="text-muted">Empty</p>';
 | 
			
		||||
                    	        }
 | 
			
		||||
                    	        output += '</div>';
 | 
			
		||||
                    	    } else if (value && typeof value === 'object' && Array.isArray(value)) {
 | 
			
		||||
@ -123,7 +122,7 @@
 | 
			
		||||
                    	                output += renderJsonSection(arrayKey, val, depth + 1);
 | 
			
		||||
                    	            });
 | 
			
		||||
                    	        } else {
 | 
			
		||||
                    	            output += '<p class="text-muted">Empty array</p>';
 | 
			
		||||
                    	            output += '<p class="text-muted">Empty</p>';
 | 
			
		||||
                    	        }
 | 
			
		||||
                    	        output += '</div>';
 | 
			
		||||
                    	    }
 | 
			
		||||
@ -132,6 +131,14 @@
 | 
			
		||||
 | 
			
		||||
                    	    return output;
 | 
			
		||||
                    	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                       function escapeHTML(s) {
 | 
			
		||||
                    	   if(s)
 | 
			
		||||
                           return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
 | 
			
		||||
                    	   
 | 
			
		||||
                    	   return null;
 | 
			
		||||
                       }
 | 
			
		||||
                       </script>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user