1. 程式人生 > 其它 >多圖片生成PDF(二)HTML生成PDF

多圖片生成PDF(二)HTML生成PDF

HTML生成PDF
一、html生成pdf需要引入jar包
在pom.xml中引入如下jar包

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.1.18</version>
</dependency>

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.4.2</version>
</dependency>

<dependency>
    <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
    <artifactId>com.lowagie.text</artifactId>
    <version>2.1.7</version>
</dependency>
<dependency>
    <groupId>com.itextpdf.tool</groupId>
    <artifactId>xmlworker</artifactId>
    <version>5.5.13</version>
    <scope>compile</scope>
</dependency>

二、Java程式碼

import cn.qcdoc.common.core.exception.BaseException;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliers;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
import com.itextpdf.tool.xml.pipeline.html.AbstractImageProvider;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
import com.itextpdf.tool.xml.pipeline.html.LinkProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * html轉PDF
 *
 * @author: wx
 * @date: 2022/11/21
 */
public class HtmlPdfDemo {
    public static String MakeHtml(List<String> listPath, String distPath, String templatePath){
        String fileame = "test.html";
        try {
            String templateContent = "";
            // 讀取模板檔案
            FileInputStream fileinputstream = new FileInputStream(templatePath);
            int length = fileinputstream.available();
            byte bytes[] = new byte[length];
            fileinputstream.read(bytes);
            fileinputstream.close();
            templateContent = new String(bytes);
            StringBuilder sbPath = new StringBuilder();
            for (String path : listPath) {
                String imgs = "<img src=\""+path+"\"/>"+"\n"+"<div style='page-break-before: always;'>\n" +
                        "\t\t</div>";
                sbPath.append(imgs);
            }
            String str = sbPath.toString().replace("\\", "/");
            //把模板頁面上的 ###image_list### 替換成 img 裡的內容
            templateContent = templateContent.replaceAll("###image_list###", str);
            // 生成的html檔案儲存路徑。
            fileame = distPath + fileame;
            // 建立檔案輸出流
            FileOutputStream fileoutputstream = new FileOutputStream(fileame);
            byte tag_bytes[] = templateContent.getBytes();
            fileoutputstream.write(tag_bytes);
            fileoutputstream.close();
        } catch (Exception e) {
            throw new BaseException("建立html失敗!");
        }
        return fileame;
    }

    /**
     * 建立PDF檔案
     * @author: wx
     * @date: 2022/12/8
     * @param: htmlPath html路徑
     * @param: outPdfPath 生成pdf路徑
     * @param: imagePath 圖片路徑
     * @returns: void
     */
    public static void writeToOutputStreamAsPDF(String htmlPath, String outPdfPath, String imagePath) throws Exception {
        File targeFile = new File(outPdfPath);
        if(targeFile.exists()) {
            targeFile.delete();
        }
        //定義pdf檔案尺寸,採用A4橫切
        Document document = new Document(PageSize.A4, 20, 20, 20, 20);// 左、右、上、下間距
        //定義輸出路徑
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outPdfPath));
        PdfReportHeaderFooter header = new PdfReportHeaderFooter("", 8, PageSize.A4);
        writer.setPageEvent(header);
        writer.addViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
        document.open();
        // HTML 設定
        HtmlPipelineContext htmlContext = getHtmlPipelineContext(imagePath, null);
        // Pipelines
        PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
        HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
        CssResolverPipeline css = new CssResolverPipeline(new StyleAttrCSSResolver(), html);
        // XML Worker
        XMLWorker worker = new XMLWorker(css, true);
        XMLParser p = new XMLParser(worker);
        p.parse(new FileInputStream(htmlPath));
        document.close();
    }

    /**
     * html 設定
     * @author: wx
     * @date: 2022/10/13
     * @param: imagePath
     * @param: cssAppliers
     * @returns: com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext
     */
    private static HtmlPipelineContext getHtmlPipelineContext(String imagePath, CssAppliers cssAppliers) {
        HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
        htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
        htmlContext.setImageProvider(new AbstractImageProvider() {
            public String getImageRootPath() {
                return imagePath;
            }
        });
        htmlContext.setLinkProvider(new LinkProvider() {
            public String getLinkRoot() {
                return imagePath;
            }
        });
        return htmlContext;
    }

    public static void main(String[] args) throws Exception {
        List<String> listPath = new ArrayList<>();
        listPath.add("1665989812537.jpg");
        listPath.add("1665989815178.jpg");
        String distPath = "upload\\depot\\electronic\\23335\\NJ01TYQABYM\\";
        String templatePath = "upload\\depot\\table.html";
        String outFilePath = "upload\\depot\\electronic\\23335\\NJ01TYQABYM\\test.pdf";
        String htmlPath = MakeHtml(listPath, distPath, templatePath);
        writeToOutputStreamAsPDF(htmlPath, outFilePath, distPath);
    }
}

/**
 * 調整pdf樣式
 *
 * @author: wx
 * @date: 2022/10/13
 */
class PdfReportHeaderFooter extends PdfPageEventHelper {

    /**
     * 頁首
     */
    public String header = "";

    /**
     * 文件字型大小,頁尾頁首最好和文字大小一致
     */
    public int presentFontSize = 12;

    /**
     * 文件頁面大小,最好前面傳入,否則預設為A4紙張
     */
    public Rectangle pageSize = PageSize.A4;

    /**
     * 模板
     */
    public PdfTemplate total;

    /**
     * 基礎字型物件
     */
    public BaseFont bf = null;

    /**
     * 利用基礎字型生成的字型物件,一般用於生成中文文字
     */
    public Font fontDetail = null;


    /**
     * @param: yeMei 頁首字串
     * @param: presentFontSize 資料體字型大小
     * @param: pageSize  頁面文件大小,A4,A5,A6橫轉翻轉等Rectangle物件
     * @returns:
     */
    public PdfReportHeaderFooter(String yeMei, int presentFontSize, Rectangle pageSize) {
        this.header = yeMei;
        this.presentFontSize = presentFontSize;
        this.pageSize = pageSize;
    }

    /**
     * 文件開啟時建立模板
     * @author: wx
     * @date: 2022/10/14
     * @param: writer
     * @param: document
     * @returns: void
     */
    public void onOpenDocument(PdfWriter writer, Document document) {
        // 頁尾 共 頁 的矩形的長寬高
        total = writer.getDirectContent().createTemplate(50, 50);
    }

    /**
     * 關閉每頁的時候,寫入頁首,寫入'第幾頁共'這幾個字。
     * @author: wx
     * @date: 2022/10/14
     * @param: writer
     * @param: document
     * @returns: void
     */
    public void onEndPage(PdfWriter writer, Document document) {
        try {
            if (bf == null) {
                bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
            }
            if (fontDetail == null) {
                fontDetail = new Font(bf, presentFontSize, Font.NORMAL);// 資料體字型
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 1.寫入頁首
        ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, new Phrase(header, fontDetail), document.left(), document.top() + 20, 0);
        // 2.寫入前半部分的 第 X頁/共
        int pageS = writer.getPageNumber();
        String foot1 = "第 " + pageS + " 頁 /共";
        Phrase footer = new Phrase(foot1, fontDetail);
        // 3.計算前半部分的foot1的長度,後面好定位最後一部分的'Y頁'這倆字的x軸座標,字型長度也要計算進去 = len
        float len = bf.getWidthPoint(foot1, presentFontSize);
        // 4.拿到當前的PdfContentByte
        PdfContentByte cb = writer.getDirectContent();
        // 5.寫入頁尾1,x軸就是(右margin+左margin + right() -left()- len)/2.0F 再給偏移20F適合人類視覺感受,否則肉眼看上去就太偏左了 ,y軸就是底邊界-20,否則就貼邊重疊到資料體裡了就不是頁尾了;注意Y軸是從下往上累加的,最上方的Top值是大於Bottom好幾百開外的。
        ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer, (document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F + 20F, document.bottom() - 16, 0);
        // 6.寫入頁尾2的模板(就是頁尾的Y頁這倆字)新增到文件中,計算模板的和Y軸,X=(右邊界-左邊界 - 前半部分的len值)/2.0F + len , y 軸和之前的保持一致,底邊界-20
        cb.addTemplate(total, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F + 20F, document.bottom() - 16); // 調節模版顯示的位置

    }

    /**
     * 關閉文件時,替換模板,完成整個頁首頁尾元件
     * @author: wx
     * @date: 2022/10/14
     * @param: writer
     * @param: document
     * @returns: void
     */
    public void onCloseDocument(PdfWriter writer, Document document) {
        // 7.最後一步了,就是關閉文件的時候,將模板替換成實際的 Y 值,至此,page x of y 製作完畢,完美相容各種文件size。
        total.beginText();
        total.setFontAndSize(bf, presentFontSize);// 生成的模版的字型、顏色
        String foot2 = " " + (writer.getPageNumber() - 1) + " 頁";
        total.showText(foot2);// 模版顯示的內容
        total.endText();
        total.closePath();
    }
}

三、生成PDF檔案

總結:
html生成pdf的時候,如果失敗了,要檢查對應的包是否引入錯誤,或者少引入jar。

本部落格借鑑了網上的例子,在這裡非常感謝其他博主的部落格,讓我在工作中解決了大量圖片生成pdf的功能。