/* 
 * Copyright (c) 1997-2025 IDRsolutions (https://www.idrsolutions.com) 
 */ 
package org.jpedal.examples; 
 
import org.jpedal.utils.LogWriter; 
 
import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.nio.file.DirectoryStream; 
import java.nio.file.FileSystems; 
import java.nio.file.Files; 
import java.nio.file.Path; 
import java.nio.file.Paths; 
import java.util.Arrays; 
import java.util.HashSet; 
import java.util.Set; 
import java.util.UUID; 
 
/** 
 * Helper class for converting Office documents to PDF using LibreOffice. 
 */ 
public final class DocumentToPDFConverter { 
 
    private static final String TEMP_DIR; 
    static { 
        String tempDir = System.getProperty("java.io.tmpdir"); 
        if (!tempDir.endsWith("/") && !tempDir.endsWith("\\")) { 
            tempDir += FileSystems.getDefault().getSeparator(); 
        } 
        TEMP_DIR = tempDir; 
    } 
 
    private DocumentToPDFConverter() { 
    } 
 
    /** 
     * Uses LibreOffice to convert an office document to PDF using the provided LibreOffice executable path. 
     * <p> 
     * Creates a file with the same name (with .pdf extension) in the same directory as the source file. 
     * <p> 
     * If the output file already exists, it will not be overwritten and an IOException will be thrown. 
     * 
     * @param document                  Office document to convert to PDF 
     * @param libreOfficeExecutablePath Absolute path for the LibreOffice executable to use to convert the document 
     * @param allowOverwrite            Boolean flag on whether the output file can be overwritten 
     * @return The exit code of the LibreOffice process (usually 0 regardless of success or failure) 
     * @throws IOException          If the LibreOffice executable was not found, the document does not exist, the document does 
     *                              not have a valid file extension, or the output file already exists (and allowOverwrite 
     *                              is set to false) 
     * @throws InterruptedException If the conversion process gets interrupted 
     */ 
    public static int convert(final File document, final String libreOfficeExecutablePath, final boolean allowOverwrite) throws IOException, InterruptedException { 
 
        if (!document.exists()) { 
            throw new FileNotFoundException(document.getAbsolutePath()); 
        } 
 
        final int idx = document.getAbsolutePath().lastIndexOf('.'); 
        if (idx < 1) { 
            throw new IOException("Document does not have a valid file extension: " + document.getAbsolutePath()); 
        } 
 
        final String outputDocumentPath = document.getAbsolutePath().substring(0, idx) + ".pdf"; 
        final File outputDocument = new File(outputDocumentPath); 
        if (outputDocument.exists()) { 
            if (allowOverwrite) { 
                outputDocument.delete(); 
            } else { 
                throw new IOException("Output file already exists: " + outputDocumentPath); 
            } 
        } 
 
        // Use a unique profile directory to allow multiple conversions simultaneously 
        final String uniqueLOProfile = (TEMP_DIR + "LO-" + UUID.randomUUID()).replace('\\', '/'); // Requires a url (not a path) 
 
        final ProcessBuilder pb = new ProcessBuilder(libreOfficeExecutablePath, 
                "-env:UserInstallation=file:///" + uniqueLOProfile, 
                "--headless", "--convert-to", "pdf", document.getName()); 
 
        pb.directory(document.getParentFile()); 
 
        try { 
            return pb.start().waitFor(); 
        } finally { 
            deleteFolder(Paths.get(uniqueLOProfile)); 
        } 
    } 
 
    /** 
     * Uses LibreOffice to convert an office document to PDF using the provided LibreOffice executable path. 
     * <p> 
     * Creates a file with the same name (with .pdf extension) in the same directory as the source file. 
     * <p> 
     * If the output file already exists, it will not be overwritten and an IOException will be thrown. 
     * 
     * @param document                  Office document to convert to PDF 
     * @param libreOfficeExecutablePath Absolute path for the LibreOffice executable to use to convert the document 
     * @return The exit code of the LibreOffice process (usually 0 regardless of success or failure) 
     * @throws IOException          If the LibreOffice executable was not found, the document does not exist, the document does 
     *                              not have a valid file extension, or the output file already exists (will not overwrite) 
     * @throws InterruptedException If the conversion process gets interrupted 
     */ 
    public static int convert(final File document, final String libreOfficeExecutablePath) throws IOException, InterruptedException { 
        return convert(document, libreOfficeExecutablePath, false); 
    } 
 
    private static final String[] FILE_TYPES = { 
            "ODT", "FODT", "ODS", "FODS", "ODP", "FODP", "ODB", "ODG", "FODG", "ODF", // LibreOffice 
            "SXW", "STW", "SXC", "STC", "SXI", "STI", "SXD", "STD", "SXM", // OpenOffice 
            "DOCX", "XLSX", "PPTX", 
            "DOC", "DOT", 
            "PPT", "PPS", "POT", 
            "XLS", "XLW", "XLT" 
    }; 
 
    private static final Set<String> FILE_TYPES_LIST = new HashSet<>(Arrays.asList(FILE_TYPES)); 
 
    /** 
     * Returns whether the provided file has a convertible file type 
     * 
     * @param file file to check 
     * @return whether provided file is convertible 
     */ 
    public static boolean hasConvertibleFileType(final File file) { 
        return hasConvertibleFileType(file.getAbsolutePath()); 
    } 
 
    /** 
     * Returns whether the provided filename has a convertible file type 
     * 
     * @param filename filename of the file to check 
     * @return whether provided file is convertible 
     */ 
    public static boolean hasConvertibleFileType(final String filename) { 
        final int idx = filename.lastIndexOf('.'); 
        return idx > 0 && FILE_TYPES_LIST.contains(filename.substring(idx + 1).toUpperCase()); 
    } 
 
    private static void deleteFolder(final Path pathToDelete) { 
 
        if (Files.isDirectory(pathToDelete)) { 
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(pathToDelete)) { 
                for (final Path path : stream) { 
                    deleteFolder(path); 
                } 
 
                Files.delete(pathToDelete); 
            } catch (final IOException e) { 
                LogWriter.error(e, "Unable to delete files in directory " + pathToDelete); 
            } 
        } else { 
            try { 
                Files.delete(pathToDelete); 
            } catch (final IOException e) { 
                LogWriter.error(e, "Unable to delete file " + pathToDelete); 
            } 
        } 
    } 
}
    
    |