缺点:原始模板变化需要重新生成pdf,重新编辑表单元素;不支持列表填充数据。
首先将调整好格式的原始 word 导出为 XML 格式,编辑 XML 模板中需要填充元素的位置,最后由程序处理先由freemarker模板工具替换元素内容,再使用doc4J进行pdf导出。
类似于 freemarker+doc4J 方式,同样需要编辑XML,导出格式相较doc4J而言有极大提升。
翻译 word 为 html 页面(当然就是手写啦,还原度很重要!),html中模板元素插入(文字填充、列表循环 freemarker 支持的全都能写),最后由程序处理先由freemarker模板工具替换元素内容,再使用html2pdf进行pdf导出。
缺点:需要中文字体库。
<dependencies> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf-itext5</artifactId> <version>9.0.3</version> </dependency> <dependency> <groupId>binarta.oss</groupId> <artifactId>groovy-template-enginex-freemarker</artifactId> <version>0.1.3</version> </dependency> <dependencies>个人以为自己的代码会自解释,就不贴太多注释了。
import cn.hutool.core.io.FileUtil; import cn.hutool.core.map.MapUtil; import com.google.common.collect.Lists; import com.itextpdf.text.pdf.BaseFont; import freemarker.cache.ByteArrayTemplateLoader; import freemarker.template.Configuration; import freemarker.template.Template; import lombok.SneakyThrows; import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextRenderer; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.StringWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; public class HtmlToPdfUtil { private static final ByteArrayTemplateLoader TEMPLATE_LOADER = new ByteArrayTemplateLoader(); // 导入需要字体库的位置哦;simsun 为 宋体 public static final String FRONT_PATH = "/usr/share/fonts/simsun.ttc"; /** * 看明白的话只用这个方法就够 */ public static ByteArrayOutputStream htmlToPdf(String templateName, Supplier<byte[]> loadTemplateSupplier, Map<String, Object> modeViewMap) { String html = xmlFormat(templateName, loadTemplateSupplier, modeViewMap); return htmlToPdf(html); } @SneakyThrows public static ByteArrayOutputStream htmlToPdf(String htmlStr) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ITextRenderer renderer = new ITextRenderer(); renderer.setDocumentFromString(htmlStr); ITextFontResolver resolver = renderer.getFontResolver(); // 堆代码 duidaima.com //添加字体,解决中文不显示的问题 resolver.addFont(FRONT_PATH, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); renderer.layout(); renderer.createPDF(outputStream); return outputStream; } public static String xmlFormat(String templateName, Supplier<byte[]> loadTemplateSupplier, Map<String, Object> modeViewMap) { if (Objects.isNull(TEMPLATE_LOADER.findTemplateSource(templateName))) { synchronized (TEMPLATE_LOADER) { if (Objects.isNull(TEMPLATE_LOADER.findTemplateSource(templateName))) { TEMPLATE_LOADER.putTemplate(templateName, loadTemplateSupplier.get()); } } } return xmlFormat(templateName, modeViewMap); } @SneakyThrows public static String xmlFormat(String templateName, Map<String, Object> modeViewMap) { Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); // 指定FreeMarker模板文件的位置 cfg.setTemplateLoader(TEMPLATE_LOADER); // 设置模板的编码格式 cfg.setEncoding(Locale.CHINA, Charset.defaultCharset().name()); // 获取模板文件 template Template template = cfg.getTemplate(templateName, Charset.defaultCharset().name()); StringWriter stringWriter = new StringWriter(); BufferedWriter writer = new BufferedWriter(stringWriter); template.process(modeViewMap, writer); return stringWriter.toString(); } }总结