多图片生成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的功能。

posted @ 2022-12-08 17:54  why0703  阅读(140)  评论(0编辑  收藏  举报