Как создать PDF-страницу HTML в Java - PullRequest
0 голосов
/ 25 июня 2019

Мне нужно сгенерировать PDF-страницу HTML. Я написал метод для этого, но он генерирует ошибку. Пожалуйста, ведите меня, где я не прав. Спасибо!

public void htmlToPdf(
    String htmlPath, 
    File pdfFile
) throws IOException, DocumentException { 
    Document document = new Document(); 
    PdfWriter writer = PdfWriter.getInstance(
        document, 
        new FileOutputStream(pdfFile)
    ); 
    document.open(); 
    XMLWorkerHelper.getInstance().parseXHtml(
        writer, 
        document, 
        new FileInputStream(htmlPath), 
        Charset.forName("UTF-8")
    ); 
    document.close(); 
}

Ошибка:

Cannot resolve method 'parseXHtml(com.lowagie.text.pdf.PdfWriter,  com.lowagie.text.Document, java.io.FileInputStream, java.nio.charset.Charset)'

1 Ответ

0 голосов
/ 25 июня 2019

То есть вы хотите создавать PDF-файлы из HTML с помощью Java?

Вот процедура, которую я использую с летающей тарелкой .

  1. Отформатируйте HTMLс CSS 2.1
  2. Напишите процесс создания PDF
  3. Создайте интерфейс генератора PDF
  4. Используйте пользовательский объект для обертывания изображений с атрибутами для дальнейшего форматирования
  5. Реализует ваш интерфейс с вашими параметрами PDF и изображениями

1.Отформатируйте HTML с помощью CSS 2.1

Примером может быть JSP с EL, любой другой шаблон (вы сможете получить сгенерированный HTML с параметрами с внутренним запросом POST) или просто статический HTML.

Вы не можете использовать пропорциональные значения, такие как em, rem, vh, vw или сложные CSS-подобные анимации.

Вы можете использовать <style> </style> тег или встроенный style= attribute

Вот пример JSP в моем веб-приложении.

<!DOCTYPE html>
<%@ page    session="false" 
            language="java" 
            contentType="text/html; charset=UTF-8" 
            pageEncoding="UTF-8" 
            isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
    <head>
        <META CHARSET="UTF-8" />
        <title>My PDF</title>
        <style>
            /* you can add reset css too */
            /* stylesheet */
            body { font-family: sans-serif; }
            .someCSSClass {}
            .anotherCSSClass {}
        </style>
    </head>
    <body>
        <div class="someCSSClass">
            <p class="anotherCSSClass" style="line-height:16px;">
                ${ param.aParameter }
            </p>

2.Напишите процесс создания PDF с интерфейсом

Зачем использовать интерфейс?Потому что в случае, если вам нужно сгенерировать дополнительные PDF-файлы из разных моделей, вам не нужно будет писать ту же логику для создания каждого PDF-файла.

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfImage;
import com.itextpdf.text.pdf.PdfIndirectObject;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import main.java.bean.ImagePDF;
import main.java.interface.PDFInterface;
import main.java.bean.Constants;
/**
 * PDFGenerator
 * Class to generate PDF (can implement Servlet).
 */
public class PDFGenerator {
    private static final String TMP_DIR = System.getProperty("java.io.tmpdir");
    /* 
     * May not be a GET, can be simple method call for local application or
     * whatever you need
     */ 
    @Override
    protected void goGet(
        HttpServletRequest request, 
        HttpServletResponse response
    ) throws IOException {
        PDFInterface pdfImplementation = null;
        /*
         * instance your PDF Model implementation according to this 
         * parameter (for example)
         */
        int pdfModel = Integer.parseInt(
            request.getParameter("requestedPDFModel")
        );
        switch (pdfModel) {
            case Constants.PDF_MODEL_1:
                pdfImplementation = new PDFImplementationOne();
                /* 
                 * You could get the image reference from GET request too, 
                 * or from database or from constants
                 */
                pdfImplementation.addImage(
                    "image1.png", 
                    120, 
                    50, 
                    "image_name1", 
                    request
                );
                break;
            case Constants.PDF_MODEL_2:
                pdfImplementation = new PDFImplementationTwo();
                pdfImplementation.addImage(
                    "image2.png", 
                    350, 
                    70, 
                    "image_name2", 
                    request
                );
                break;
            default :
                System.out.println("Cannot find an implementation for the requested PDF.");
                return null;
        }
        String html = null;
        /*
            Get the HTML from an URL : if your implementation returns null
            then you can for example decide to get the HTML from a file in your implementation
        */
        if (pdfImplementation.getUrl(request) != null) {
            // Send POST request to generate the HTML from a template (JSP, JSF, Thymeleaf, ...)
            URLConnection connection = new URL(
                pdfImplementation.getUrl(request)
                +pdfImplementation.getEncodedQueryString()
            ).openConnection();
            connection.setDoOutput(true); // POST : remove this to do a GET
            connection.setRequestProperty("Accept-Charset", "UTF-8");
            connection.setRequestProperty(
                "Content-Type", 
                "application/x-www-form-urlencoded;charset=UTF-8"
            );
            try (OutputStream output = connection.getOutputStream()) {
                output.write(
                    pdfImplementation
                        .getEncodedQueryString()
                        .getBytes(StandardCharsets.UTF_8)
                );
            }
            // Open an input stream on the response
            BufferedReader in = new BufferedReader(
                new InputStreamReader(connection.getInputStream())
            );
            StringBuilder sb = new StringBuilder();
            // A line in our generated HTML
            String inputLine;
            // Read all HTML lines and concatenate
            while ((inputLine = in.readLine()) != null) { 
                sb.append(inputLine); 
            }
            html = sb.toString();
            in.close();
        }
        // Get the HTML from a File
        else {
            html = String.join(
                "", 
                Files.readAllLines(pdfImplementation.getHTMLFile().toPath())
            );
        }
        // Create a temp file to make the PDF
        File tempPDFFile = new File(
            TMP_DIR + pdfImplementation.getGeneratedPDFFileName()
        );
        if (!tempPDFFile.exists()) { tempPDFFile.createNewFile(); }
        FileOutputStream fos = new FileOutputStream(tempPDFFile);
        // Output the HTML to the temp PDF file
        new ITextRenderer() {{
            setDocumentFromString(html);
            layout();
            createPDF(fos);
        }};
        fos.close();
        // Create your final PDF file
        File pdf = new File(pdfImplementation.getPDFFilename());
        // Add images if needed
        addImageToPDF(pdfImplementation, tempPDFFile, pdf);
        // Write in response if you need servlet implementation
        writePDFContentToResponse(pdf, response);
    }
    /**
     * writePDFContentToResponse
     * @param pdf : the final PDF file
     * @param response :  a HTTPServletResponse to write PDF file bytes
     * @throws IOException
     */
    void writePDFContentToResponse(
        File pdf, 
        HttpServletResponse response
    ) throws IOException {
        InputStream fis = new FileInputStream(pdf);
        String mimeType = getServlet().getServletContext()
            .getMimeType(pdf.getAbsolutePath());
        response.setContentType(
            mimeType != null ? mimeType : "application/octet-stream"
        );
        response.setContentLength((int) pdf.length());
        response.setHeader(
            "Content-Disposition", 
            "attachment; filename="+pdf.getName()+".pdf"
        );
        ServletOutputStream os = response.getOutputStream();
        byte[] bufferData = new byte[1024];
        int read = 0;
        while((read = fis.read(bufferData)) != -1) { 
            os.write(bufferData, 0, read); 
        }
        os.flush();
        os.close();
        fis.close();
        response.flushBuffer();
        Files.delete(pdf.toPath());
    }
    /**
     * addImageToPDF
     * 
     * @param pdfImplementation : the pdfImplementation to get the array of
     * custom image objects ImagePDF.
     * @param tempPDFFile : the temp PDF file with already HTML content 
     * converted.
     * @param pdf : the final PDF file which will have images stamped.
     * @throws DocumentException
     * @throws IOException
     */
    void addImageToPDF(
        PDFInterface pdfImplementation, 
        File tempPDFFile, 
        File pdf
    ) throws DocumentException, IOException {
        PdfReader reader = new PdfReader(new FileInputStream(tempPDFFile));
        PdfStamper stamper = new PdfStamper(
            reader, 
            new FileOutputStream(pdf)
        );
        for (ImagePDF img: pdfImplementation.getImages()) {
            Image image = img.getImage();
            image.scalePercent(img.getScale());
            PdfImage stream = new PdfImage(image, "", null);
            stream.put(
                new PdfName("ITXT_SpecialId"), 
                new PdfName("123456789")
            );
            PdfIndirectObject ref = stamper.getWriter().addToBody(stream);
            image.setDirectReference(ref.getIndirectReference());
            image.setAbsolutePosition(
                img.getWidthPosition(), 
                img.getHeightPosition()
            );
            PdfContentByte over = stamper.getOverContent(1);
            over.addImage(image);
        }
        stamper.close();
        reader.close();
    }
}

3. Создайте интерфейс генератора PDF

import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import com.itextpdf.text.BadElementException;
/**
* PDFInterface
* Interface to define the behavior a PDF model has to implement.
*/
public interface PDFInterface {
    /**
     * getUrl
     * @param request the HTTPServletRequest to fetch parameters for the PDF
     * @return the URL target to make a HTTP POST request to get the generated
     * HTML (for example if you are making a HTTP POST on a JSP to generate
     * HTML dynamically.
     */
    String getUrl(HttpServletRequest request);
    /**
     * getHTMLFile
     * @return return the HTML file from the local storage to be read to get 
     * the HTML.
     */
    File getHTMLFile();
    /**
     * setParametres
     * @param object : an object or a list of objects to be encoded to the
     * query String to generate the PDF.
     */
    void setParametres(Candidat candidat);

    String getEncodedQueryString();
    /**
     * getImages
     * @return a custom ImagePDF object with needed attributes to add an image
     * after the PDF has been generated has the HTML cannot be read to get
     * image during the generation of the PDF.
     */
    List<ImagePDF> getImages();
    /**
     * addImage
     * @param url : the URL to get the image
     * @param x : the X position
     * @param y : the Y position
     * @param name : the name of the image
     * @param request : the HTTPServletRequest to generate the relative link 
     * to fetch the image.
     * @param scale : the scale of the image
     * @throws BadElementException
     * @throws IOException
     */
    void addImage(
        String url,
        float x,
        float y,
        String name,
        HttpServletRequest request,
        float scale
    ) throws BadElementException, IOException;
    /**
     * getPDFFilename
     * @return : the name of the PDF file to be generated
     */
    String getPDFFilename();
}

4.Объект ImagePDF (в случае, если вам нужно добавить изображение в ваш PDF)

import java.io.IOException;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
/**
 * ImagePDF
 * Class for a custom ImagePDF object to fit needs to stamp an image on a
 * generated PDF (URI to get the image, scale, positions x y ...).
 */
public class ImagePDF implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private Image image;
    private float widthPosition;
    private float heightPosition;
    private String name;
    private Float scale;
    /**
     * ImagePDF
     * @param urlImage : the URL to fetch the image
     * @param heightPosition : the y position on the PDF canvas
     * @param widthPosition : the x position on the PDF canvas
     * @param name : the name of the image
     * @param scale : the scale of the image on the PDF canvas
     * @throws BadElementException
     * @throws IOException
     */
    public ImagePDF(
        String urlImage,
        float widthPosition,
        float heightPosition,
        String name,
        Float scale
    ) throws BadElementException, IOException {
        this.image = Image.getInstance(urlImage);
        this.heightPosition = heightPosition;
        this.widthPosition = widthPosition;
        this.name = name;
        this.scale = scale;
    }
    // Getters and setters ...

5. Реализует ваш интерфейс для ваших параметров PDF

(используется в примере выше)

/**
 * PDFImplementationOne
 * The PDFImplementation to generate a specific PDF.
 */
public class PDFImplementationOne implements PDFInterface {
    private static final String PARAM_1 = "param1";
    private static final String PARAM_2 = "param2";
    private Map<String, String> parameters;
    private List<ImagePDF> images;
    /**
     * PDFImplementationOne
     * You can pass service to add information retreival from DB or objects to 
     * pass to parameters in the constructor if needed.
     */
    public PDFImplementationOne (CustomObject aParameter) {
        this.parameters = new HashMap<>();
        this.images = new ArrayList<>();
        // in case you need parameters, passed in constructor
        setParametres(aParameter); 
    }
    /* (non-Javadoc)
     * @see main.java.interface.PDFInterface#getUrl()
     */
    @Override
    public String getUrl(HttpServletRequest request) {
        /* 
         * This is an example in case your generate your HTML from JSP with 
         * parameters, if it is from static file then return null
         */
        StringBuilder sb = new StringBuilder("http://");
        sb.append(request.getServerName());
        sb.append((request.getServerName().startsWith("127.0.0")?":8080":""));
        sb.append("/MyApp/urlToJSP");
        return sb.toString();
    }
    /*
     * (non-Javadoc)
     * @see main.java.interface.PDFInterface#addImage(
     *  java.lang.String, 
     *  float, 
     *  float, 
     *  java.lang.String, 
     *  javax.servlet.http.HttpServletRequest,
     *  float scale
     * )
     */
    @Override
    public void addImage(
        String fileName,
        float x,
        float y,
        String name,
        HttpServletRequest request
    ) {
        /* 
         * Here I get the image from a ressource server but you can read the 
         * image from local storage as well
         */
        StringBuilder url = new StringBuilder("http://");
        url.append(request.getServerName());
        url.append(request.getServerName().startsWith("127.0.0")?":8080":"");
        url.append("/MyApp/img/");
        url.append(fileName);
        try {
            ImagePDF image = new ImagePDF(url.toString(), x, y, name, scale);
            images.add(image);
        }
        catch (BadElementException | IOException e) {
            System.out.println(Cannot set image for PDF "+url.toString());
        }
    }
    /* (non-Javadoc)
     * @see main.java.interface.PDFInterface#getImages()
     */
    @Override
    public List<ImagePDF> getImages() {
        return this.images;
    }
    /* (non-Javadoc)
     * @see main.java.interface.PDFInterface#setParameters(
     *  CustomObject customObject
     * )
     */
    @Override
    public void setParametres(CustomObject customObject) {
        parametres.put(PARAM_1, customObject.getAttribute().toString());
        // may have other parameters ...
    }
    /* (non-Javadoc)
     * @see model.bean.ResultatsEcritsPDF#getEncodedQueryString()
     */
    @Override
    public String getEncodedQueryString() {
        /* 
         * Create the queryString to do further HTTP POST or GET to fetch the 
         * generated HTML with parameters
         */
        StringBuilder queryStringBuilder = new StringBuilder("?");
        parameters.entrySet().stream().forEach(e -> {
            queryStringBuilder.append(e.getKey());
            queryStringBuilder.append("=");
            try {
                queryStringBuilder.append(
                    URLEncoder.encode(
                        e.getValue() == null 
                            ? "" 
                            : e.getValue(), 
                        StandardCharsets.UTF_8.name()
                    )
                );
            }
            catch (UnsupportedEncodingException e1) {
                queryStringBuilder.append("");
            }
            queryStringBuilder.append("&");
        });
        // Remove the last &
        return queryStringBuilder.toString().substring(
            0, 
            queryStringBuilder.toString().length()-1
        );
    }
    /* (non-Javadoc)
     * @see model.bean.PDFInterface#getHTMLFile()
     */
    @Override
    public File getHTMLFile() {
        return new File("/path/to/myHTMLFile.html");
    }
    /* (non-Javadoc)
     * @see model.bean.PDFInterface#getPDFFilename()
     */
    @Override
    public String getPDFFilename() {
        return "myPDF.pdf";
    }
}

Скажите, нужно ли это уточнить?

...