OpenPdf组件替换Itext方案

OpenPdf简介

OpenPDF是一个Java库,用于创建和编辑具有LGPL和MPL开源许可证的PDF文件。
官方git地址 链接: https://github.com/LibrePDF/OpenPDF

OpenPDF是iText的LGPL/MPL开源后续版本,它基于iText 4svn标记的fork、fork。我们欢迎其他开发者的贡献。请随时向这个GitHub存储库提交pull-requests和错误报告。

由于Itext 5.0和7.0要求使用它的项目也得开源,但这基本上不符合企业的项目。

上代码

若项目已有itext的相关构建代码,基本上就是重新引入包即可,相关接口的名字基本上一样。

这里引用一个demo创建相关接口。
项目结构为SpringBoot +thymeleaf+maven项目,如图所示:
项目结构

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>openPdfDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>openPdfDemo</name>
    <description>openPdfDemo</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.librepdf</groupId>
            <artifactId>openpdf</artifactId>
            <version>1.3.29</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1、pdf构建接口创建

创建PdfCreator接口类,对操作步骤进行抽象化,方便后续扩展。

import com.lowagie.text.DocumentException;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

/**
 * pdf生成器接口
 */
public interface PdfCreator {
    /**
     * 初始化
     *
     * @return PdfCreator
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    PdfCreator init() throws DocumentException, IOException;

    /**
     * 构建pdf
     *
     * @param map 导出数据参数
     * @return ByteArrayOutputStream
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    ByteArrayOutputStream creator(Map<String, Object> map) throws DocumentException, IOException;

    /**
     * 资源关闭
     *
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    void close() throws DocumentException, IOException;
}

创建 PdfCreateStrategy接口:

import com.lowagie.text.DocumentException;

import java.io.IOException;
import java.util.Map;

/**
 * pdf 构建策略-用于不同pdf时指向各自的pdf构建实现
 */
public interface PdfCreateStrategy {
    /**
     * 够构建方法
     *
     * @param map 导出数据所需参数
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    void execute(Map<String, Object> map) throws DocumentException, IOException;
}

创建抽象实现类AbstractPdfCreator,将共用部分进行提取

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.Header;
import com.lowagie.text.PageSize;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfWriter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

/**
 * pdf构建抽象生成类
 */
public abstract class AbstractPdfCreator implements PdfCreator, PdfCreateStrategy {
    /**
     * 页框大小,采用的类型有 "crop", "trim", "art" and "bleed".,这里采用 "art"
     */
    private static final String BOX_NAME = "art";
    /**
     * 文档类
     */
    protected Document document;
    /**
     * 基础字体
     */
    protected BaseFont baseFont;
    /**
     * 字体样式
     */
    protected Font font;
    /**
     * 字体大小
     */
    protected int fontSize;
    /**
     * 页面框架大小配置
     */
    protected Rectangle pageSize;
    /**
     * 字节输出流
     */
    private ByteArrayOutputStream byteArrayOutputStream;

    /**
     * pdf构建操作转为字节流
     *
     * @param map 导出数据参数
     * @return ByteArrayOutputStream
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    @Override
    public ByteArrayOutputStream creator(Map<String, Object> map) throws DocumentException, IOException {
        execute(map);
        close();
        return byteArrayOutputStream;
    }

    /**
     * pdf构建执行
     *
     * @param map 导出数据所需参数
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    @Override
    public abstract void execute(Map<String, Object> map) throws DocumentException, IOException;

    /**
     * 默认初始化
     *
     * @return PdfCreator
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    @Override
    public PdfCreator init() throws DocumentException, IOException {
        return init(baseFont, fontSize, font, pageSize, null);
    }

    /**
     * 自定义初始化监听
     *
     * @param helper 自定义文档操作监听
     * @return PdfCreator
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    public PdfCreator init(PdfPageEventHelper helper) throws DocumentException, IOException {
        return init(baseFont, fontSize, font, pageSize, helper);
    }

    /**
     * 自定义初始化参数
     *
     * @param baseFont           基础字体
     * @param fontSize           字体大小
     * @param font               字体样式
     * @param rectangle          页面大小设置
     * @param pdfPageEventHelper 文档操作监听
     * @return PdfCreator
     * @throws DocumentException 文档操作异常
     * @throws IOException       IO操作异常
     */
    public PdfCreator init(BaseFont baseFont, int fontSize, Font font, Rectangle rectangle, PdfPageEventHelper pdfPageEventHelper) throws DocumentException, IOException {
        this.baseFont = null != baseFont ? baseFont : BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", Boolean.FALSE);
        this.fontSize = 0 != fontSize ? fontSize : Header.PARAGRAPH;
        this.font = null != font ? font : new Font(this.baseFont, this.fontSize, Font.NORMAL);
        this.pageSize = null != pageSize ? pageSize : PageSize.A4;

        document = new Document();
        document.setMargins(30, 30, 50, 90);
        byteArrayOutputStream = new ByteArrayOutputStream();
        PdfWriter writer = PdfWriter.getInstance(document, byteArrayOutputStream);
        writer.setBoxSize(BOX_NAME, this.pageSize);
        if (null != pdfPageEventHelper) {
            writer.setPageEvent(pdfPageEventHelper);
        }
        document.open();
        return this;
    }

    /**
     * 资源关闭
     *
     * @throws DocumentException
     * @throws IOException       IO操作异常
     */
    @Override
    public void close() throws IOException {
        document.close();
        byteArrayOutputStream.close();
    }
}

自定义监听器PdfPageEventListener创建

import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.CMYKColor;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
import org.springframework.util.StringUtils;

import java.io.IOException;

/**
 * pdf页面自定义监听器
 */
public class PdfPageEventListener extends PdfPageEventHelper {
    /**
     * pdf标题字体大小
     */
    private int headerFontSize;
    /**
     * pdf标题宽度
     */
    private Float headerWidth;
    /**
     * pdf标题高度
     */
    private Float headerHeight;
    /**
     * pdf标题模板
     */
    private PdfTemplate headerTemplate;
    /**
     * pdf标题基础字体
     */
    private BaseFont headerBaseFont;
    /**
     * pdf标题字体样式
     */
    private Font headerFont;
    /**
     * pdf编号(一般左上角都会有类似流水号的编号,可有可无)
     */
    private String noticeCode;
    /**
     * pdf签字(可有可无,根据实际情况进行赋值)
     */
    private String signature;

    /**
     * 构建器,进行基本信息初始化
     *
     * @param headerFontSize pdf标题字体大小
     * @param headerWidth    pdf标题宽度
     * @param headerHeight   pdf标题高度
     * @param headerBaseFont pdf标题基础字体
     * @param headerFont     pdf标题字体样式
     * @param noticeCode     pdf编号
     * @param signature      pdf签字
     */
    public PdfPageEventListener(int headerFontSize, Float headerWidth, Float headerHeight, BaseFont headerBaseFont, Font headerFont, String noticeCode, String signature) {
        this.headerFontSize = headerFontSize;
        this.headerWidth = headerWidth;
        this.headerHeight = headerHeight;
        this.headerBaseFont = headerBaseFont;
        this.headerFont = headerFont;
        this.noticeCode = noticeCode;
        this.signature = signature;
    }

    /**
     * pdf监视器构建
     *
     * @return Builder
     */
    public static Builder build() {
        return new Builder();
    }

    /**
     * 重写文档初始化的模板
     */
    @Override
    public void onOpenDocument(PdfWriter writer, Document document) {
        headerTemplate = writer.getDirectContent().createTemplate(headerWidth, headerHeight);
    }

    /**
     * 重写每一页结束事件,等所有内容加载完毕后,进行页眉的数据加载和赋值
     */
    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        try {
            if (headerBaseFont == null) {
                headerBaseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", Boolean.FALSE);
            }
            if (headerFont == null) {
                headerFont = new Font(headerBaseFont, headerFontSize, Font.NORMAL);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取文档内容
        PdfContentByte pdfContentByte = writer.getDirectContent();
        float left = document.left();
        float right = document.right();
        float top = document.top();
        float bottom = document.bottom();
        int pageNumber = writer.getPageNumber();
        String noticeCodeText = StringUtils.isEmpty(noticeCode) ? "" : "编号:" + noticeCode;
        String previousHeaderText = "第 " + pageNumber + " 页 /共";
        Phrase headerPhrase = new Phrase(previousHeaderText, headerFont);
        Phrase noticePhrase = new Phrase(noticeCodeText, headerFont);
        Phrase signaturePhrase = new Phrase(signature, headerFont);
        float noticeLen = headerBaseFont.getWidthPoint(noticeCodeText, headerFontSize);
        float headerLen = headerBaseFont.getWidthPoint(previousHeaderText, headerFontSize);
        float signatureLen = headerBaseFont.getWidthPoint(signature, headerFontSize);
        float x0 = left + noticeLen / 2;
        float x1 = right - headerLen / 2 - 20F;
        float x2 = right - 20F;
        float x3 = right - signatureLen / 2;
        float y1 = top + 10F;
        float y2 = bottom - 40F;
        ColumnText.showTextAligned(pdfContentByte, Element.ALIGN_CENTER, noticePhrase, x0, y1, Element.HEADER);
        ColumnText.showTextAligned(pdfContentByte, Element.ALIGN_CENTER, headerPhrase, x1, y1, Element.HEADER);
        ColumnText.showTextAligned(pdfContentByte, Element.ALIGN_CENTER, signaturePhrase, x3, y2, Element.HEADER);
        pdfContentByte.addTemplate(headerTemplate, x2, y1);

        float lineY = top + 6f;

        CMYKColor magentaColor = new CMYKColor(1.f, 1.f, 1.f, 1.f);
        pdfContentByte.setColorStroke(magentaColor);
        pdfContentByte.moveTo(left, lineY);
        pdfContentByte.lineTo(left, lineY);
        pdfContentByte.closePathStroke();
    }

    /**
     * 文档结束后,将总页码放在文档上
     */
    @Override
    public void onCloseDocument(PdfWriter writer, Document document) {
        headerTemplate.beginText();
        headerTemplate.setFontAndSize(headerBaseFont, headerFontSize);
        int pageNumber = writer.getPageNumber() - 1;
        String behindHeaderText = " " + pageNumber + " 页";
        headerTemplate.showText(behindHeaderText);
        headerTemplate.endText();
        headerTemplate.closePath();
    }

    /**
     * 自定义一个构建器,设置页面相关信息
     */
    public static class Builder {

        private int headerFontSize;
        private Float headerWidth;
        private Float headerHeight;
        private PdfTemplate headerTemplate;
        private BaseFont headerBaseFont;
        private Font headerFont;
        private String noticeCode;
        private String signature;

        Builder() {
            headerFontSize = Font.DEFAULTSIZE;
            headerWidth = 50F;
            headerHeight = 50F;
            try {
                headerBaseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", Boolean.FALSE);
            } catch (IOException e) {
                e.printStackTrace();
            }
            headerFont = new Font(headerBaseFont, headerFontSize, Font.NORMAL);
            noticeCode = "";
            signature = "";
        }

        public Builder setHeaderFontSize(int headerFontSize) {
            this.headerFontSize = headerFontSize;
            return this;
        }

        public Builder setHeaderWidth(Float headerWidth) {
            this.headerWidth = headerWidth;
            return this;

        }

        public Builder setHeaderHeight(Float headerHeight) {
            this.headerHeight = headerHeight;
            return this;
        }

        public Builder setHeaderTemplate(PdfTemplate headerTemplate) {
            this.headerTemplate = headerTemplate;
            return this;
        }

        public Builder setHeaderBaseFont(BaseFont headerBaseFont) {
            this.headerBaseFont = headerBaseFont;
            return this;
        }

        public Builder setHeaderFont(Font headerFont) {
            this.headerFont = headerFont;
            return this;
        }

        public Builder setNoticeCode(String noticeCode) {
            this.noticeCode = noticeCode;
            return this;
        }

        public Builder setSignature(String signature) {
            this.signature = signature;
            return this;
        }

        public PdfPageEventListener build() {
            return new PdfPageEventListener(headerFontSize, headerWidth, headerHeight, headerBaseFont, headerFont, noticeCode, signature);
        }

    }
}

2、实践

比如我们模拟用户信息打印。

创建用户信息实体类UserModel

public class UserModel {
    private String name;
    private int age;
    private String sex;
    private String nativePlace;
    private String national;
    private String education;
    private String remark;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getNativePlace() {
        return nativePlace;
    }

    public void setNativePlace(String nativePlace) {
        this.nativePlace = nativePlace;
    }

    public String getNational() {
        return national;
    }

    public void setNational(String national) {
        this.national = national;
    }

    public String getEducation() {
        return education;
    }

    public void setEducation(String education) {
        this.education = education;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }
}

创建适用于用户信息打印的pdf构建实现类UserInfoPdfCreator

import com.demo.openpdfdemo.model.UserModel;
import com.demo.openpdfdemo.pdfCreator.core.AbstractPdfCreator;
import com.demo.openpdfdemo.pdfCreator.core.HtmlParserUtil;
import com.lowagie.text.Chunk;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * 用户信息导出为pdf
 */
public class UserInfoPdfCreator extends AbstractPdfCreator {

    @Override
    public void execute(Map<String, Object> map) throws DocumentException, IOException {
        //map中可以存放实体类信息,以及其他各种逻辑数据信息等
        UserModel user = (UserModel) map.get("data");
        Font titleFont = new Font(baseFont, 14, Font.BOLD);
        Paragraph title = new Paragraph("用户信息表", titleFont);
        title.setAlignment(Element.ALIGN_CENTER);
        title.setSpacingAfter(10F);
        document.add(title);
        Paragraph paragraphText = new Paragraph();
        Font font1 = new Font(baseFont, fontSize, Font.NORMAL);
        paragraphText.add(new Chunk("此表为用户基本信息,请查看", font1));
        paragraphText.setAlignment(Element.ALIGN_LEFT);
        paragraphText.setFirstLineIndent(fontSize * 2);
        document.add(paragraphText);

        PdfPTable table = new PdfPTable(4);
        table.setTotalWidth(530);
        table.setWidths(new int[]{20, 30, 22, 28});
        table.setSpacingBefore(20);
        table.setWidthPercentage(100);
        table.setLockedWidth(true);

        Paragraph p1 = new Paragraph("姓名", font);
        PdfPCell cell1 = new PdfPCell(p1);
        cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell1);
        Paragraph p2 = new Paragraph(user.getName(), font);
        PdfPCell cell2 = new PdfPCell(p2);
        cell2.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell2);

        Paragraph p3 = new Paragraph("年龄", font);
        PdfPCell cell3 = new PdfPCell(p3);
        cell3.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell3);
        Paragraph p4 = new Paragraph(String.valueOf(user.getAge()), font);
        PdfPCell cell4 = new PdfPCell(p4);
        cell4.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell4);

        Paragraph p5 = new Paragraph("性别", font);
        PdfPCell cell5 = new PdfPCell(p5);
        cell5.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell5);
        Paragraph p6 = new Paragraph(user.getSex(), font);
        PdfPCell cell6 = new PdfPCell(p6);
        cell6.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell6);

        Paragraph p7 = new Paragraph("籍贯", font);
        PdfPCell cell7 = new PdfPCell(p7);
        cell7.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell7);
        Paragraph p8 = new Paragraph(user.getSex(), font);
        PdfPCell cell8 = new PdfPCell(p8);
        cell8.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell8);

        Paragraph p9 = new Paragraph("民族", font);
        PdfPCell cell9 = new PdfPCell(p9);
        cell9.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell9);
        Paragraph p10 = new Paragraph(user.getSex(), font);
        PdfPCell cell10 = new PdfPCell(p10);
        cell10.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell10);

        Paragraph p11 = new Paragraph("学历", font);
        PdfPCell cell11 = new PdfPCell(p11);
        cell11.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell11);
        Paragraph p12 = new Paragraph(user.getSex(), font);
        PdfPCell cell12 = new PdfPCell(p12);
        cell12.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell12);

        Paragraph p13 = new Paragraph("备注", font);
        PdfPCell cell13 = new PdfPCell(p13);
        cell13.setVerticalAlignment(Element.ALIGN_MIDDLE);
        table.addCell(cell13);

        PdfPCell cell14 = new PdfPCell();
        List<Element> elements = HtmlParserUtil.htmlParseToElementList(user.getRemark(), font);
        if (!CollectionUtils.isEmpty(elements)) {
            for (Element element : elements) {
                cell14.addElement(element);
            }
        }
        cell14.setColspan(3);
        table.addCell(cell14);

        table.setSplitLate(false);
        document.add(table);
    }
}

实际项目中有的字段信息为富文本类型的,数据库中存储的是html片段,但这个跟整个html转pdf还不一样,比如这里的用户的备注信息,这里要求这个备注信息插入到pdf中,其他字段不是html片段。官方提供的是通过HTMLWorker来实现,看源码,提供的注释也是少之甚少啊。
在这里插入图片描述
创建HtmlParserUtil工具类:

import com.lowagie.text.Chunk;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.html.simpleparser.HTMLWorker;
import com.lowagie.text.html.simpleparser.StyleSheet;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPRow;
import com.lowagie.text.pdf.PdfPTable;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 实现对html片段解析为Document中所需的Element信息
 */
public class HtmlParserUtil {
    /**
     * 实现对html片段解析
     *
     * @param html html代码
     * @param font 字体
     * @return List<Element>
     * @throws IOException IO操作异常
     */
    public static List<Element> htmlParseToElementList(String html, Font font) throws IOException {
        if (!"".equals(html)) {
            //字符流
            StringReader stringReader = new StringReader(html);
            //用于存放字体样式实现类,这里为空即可,通过测试,发现用其他字体无法正常中文,故这里需要进行对baseFont重新定义
            Map<String, Object> map = new HashMap<>();
//            map.put("font_factory", new FontFactoryImp());
            List<Element> elements = HTMLWorker.parseToList(stringReader, new StyleSheet(), map);
            if (!CollectionUtils.isEmpty(elements)) {
                //解析数据获取具体的element
                List<Element> chunks = new ArrayList<>();
                for (Element element : elements) {
                    if (element instanceof com.lowagie.text.List) {
                        com.lowagie.text.List list = (com.lowagie.text.List) element;
                        chunks.addAll(list.getChunks());
                    } else if (element instanceof PdfPTable) {
                        PdfPTable pdfPTable = (PdfPTable) element;
                        ArrayList<PdfPRow> rows = pdfPTable.getRows();
                        for (PdfPRow row : rows) {
                            for (PdfPCell cell : row.getCells()) {
                                List<Element> compositeElements = cell.getCompositeElements();
                                for (Element e : compositeElements) {
                                    chunks.addAll(e.getChunks());
                                }
                            }
                        }
                    } else {
                        chunks.addAll(element.getChunks());
                    }
                }
                if (!CollectionUtils.isEmpty(chunks)) {
                    //将所有的Font的BaseFont重新定义,Font跟pdf其他元素一样即可,其他的是正常显示的。
                    //目前没有找到跟Itext一样的全局设置的接口,只能检索每个元素重新设置字体
                    for (Element chunk : chunks) {
                        if (chunk instanceof Chunk) {
                            Font chunkFont = ((Chunk) chunk).getFont();
                            Font newFont = new Font(font.getBaseFont(), chunkFont.getSize(), chunkFont.getStyle(), chunkFont.getColor());
                            ((Chunk) chunk).setFont(newFont);
                        }
                    }
                }
            }
            return elements;
        }
        return new ArrayList<>();
    }
}

以上代码可以看出对font进行了相关处理,若不处理的话,中文是不能正常显示的。很多人就疑问itext可针对html进行字体注册和样式设置,这个没有吗?其实是有的;
再看源码,第二个参数就是存储样式的,第三个参数是自定义字体注册的实现类
在这里插入图片描述
worker.setInterfaceProps(interfaceProps);我们进一步看源码
在这里插入图片描述
可以看到FontProvider是根据需要进行自定义实现类,它默认有一个实现类
在这里插入图片描述
这里面有很多方法,可以通过各种方式来将本地或者指定路径的字体加载进来,如
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我尝试把本地的其他中文字体加载进来后,并且在html标签上设置属性font-family后,还是pdf无法显示中文,这也是我为啥在工具类中直接获取解析后的信息直接替换的原因。

好,继续操作,创建UserService ,方法为printUserInfo,用户信息可以自行实现加载,这里是模拟。

import com.demo.openpdfdemo.model.UserModel;
import com.demo.openpdfdemo.pdfCreator.PdfPageEventListener;
import com.demo.openpdfdemo.pdfCreator.UserInfoPdfCreator;
import com.demo.openpdfdemo.pdfCreator.core.AbstractPdfCreator;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;

@Service
public class UserService {
    /**
     * 用户信息打印
     *
     * @param response
     * @throws IOException
     */
    public void printUserInfo(HttpServletResponse response) throws IOException {
        UserModel model = new UserModel();
        model.setName("张三");
        model.setAge(12);
        model.setEducation("本科");
        model.setNational("汉族");
        model.setSex("男");
        model.setNativePlace("北京市");
        model.setRemark("<p>此人学习习惯很好</p><p><strong>非常优秀</strong></p>爱好<ul><li>篮球</li><li>乒乓球</li></ul><table border=\"1\" style=\"border-collapse: collapse\"><tr><th style=\"width: 30%;color: aquamarine\">毕业学校</th> <th style=\"width: 30%\">专业</th> </tr><tr><td style=\"width: 30%\">清华</td> <td style=\"width: 30%\">计算机</td> </tr></table>");
        Map<String, Object> map = new HashMap<>();
        map.put("data", model);
        String signature = "签字:________________" + "日期:" + LocalDate.now();
        PdfPageEventListener listener = PdfPageEventListener.build().setNoticeCode("0001").setSignature(signature).build();
        AbstractPdfCreator pdfCreator = new UserInfoPdfCreator();
        ByteArrayOutputStream outputStream = pdfCreator.init(listener).creator(map);
        response.reset();
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("content-disposition", "attachment;filename=111.pdf");
        response.setContentLength(outputStream.size());
        OutputStream output = response.getOutputStream();
        outputStream.writeTo(output);
        output.flush();
        output.close();
    }
}

创建controller :UserController

import com.demo.openpdfdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping(path = "/userPrint", method = RequestMethod.POST)
    @ResponseBody
    public void userPrint(HttpServletResponse response) {
        try {
            userService.printUserInfo(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @RequestMapping("/hello")
    public String index(){
        return "index";
    }
}

创建一个页面,模拟一下下载操作,创建index.html,默认是加载templetes文件夹的页面,文件夹没有自行创建即可。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OpenPdf测试</title>
</head>
<body>
<h1>OpenPdf测试</h1>
<form action="/user/userPrint" method="post">

    <input type="submit" value="下载"/>

</form>
</body>
</html>

application.yml配置,我这里简单

server:
  port: 8080

启动服务,访问
在这里插入图片描述
点击下载,看一下内容。备注里的信息全是中文都可正常显示。
在这里插入图片描述

gitee项目地址

链接: https://gitee.com/yang1112/openPdfDemo.git

关于源码本人还在研究中,后续会根据实际用到的进行相关代码解析和案例讲解

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
itextpdf可以通过使用PdfStamper类中的方法来替换PDF中的图片。根据引用中的代码示例,可以看到在manipulatePdf方法中,通过PdfReader读取PDF文件,然后获取第一页的信息。在获取到的信息中,可以找到包含图片的部分,并将其替换为新的图片。具体的替换步骤如下: 1. 使用PdfReader类读取原始的PDF文件。 2. 获取需要替换的页的信息,可以通过getPageN()方法获取指定页的PdfDictionary对象。 3. 从PdfDictionary中获取到包含图片的PdfObject对象。 4. 判断获取到的对象是否为PRStream类型,如果是,则表示该对象是一个流对象。 5. 通过PdfReader.getStreamBytes()方法获取到流对象的字节数据。 6. 将获取到的字节数据转换为String类型,并使用replace()方法替换其中的图片信息。 7. 将替换后的字节数据重新设置回流对象。 8. 使用PdfStamper类将修改后的PDF文件保存到指定的路径中。 需要注意的是,替换图片时要保证替换的图片与原始图片的大小和格式相同,以确保替换成功。此外,也可以根据具体的需求对代码进行适当的修改,以满足特定的替换图片的要求。引用中提到对图片替换进行了一些修改,以避免替换失败的情况。 总结起来,使用itextpdf替换PDF中的图片可以通过读取PDF文件、替换图片信息并保存修改后的PDF文件来实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [itext5实现参数和图片替换](https://blog.csdn.net/FU250/article/details/80870246)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值