1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > html pdf支持css%写法吗 flying-saucer-pdf终于完美解决了(中文问题 换行问题 分

html pdf支持css%写法吗 flying-saucer-pdf终于完美解决了(中文问题 换行问题 分

时间:2023-11-01 12:44:19

相关推荐

html pdf支持css%写法吗 flying-saucer-pdf终于完美解决了(中文问题 换行问题 分

集成freemarker+flying-saucer-pdf+itext,通过html模板生成PDF

折腾了很久,flying-saucer-pdf终于完美解决了(中文问题,换行问题,页眉页脚,水印),html+css控制pdf样式

一共集成到两个类中:Generator & PDFBuilder,具体看详细的代码注释,相关文件路径自行修改

转黄方法入口:Generator.pdfGeneratePlus

1.引入相关java

org.xhtmlrenderer

flying-saucer-pdf-itext5

9.1.18

com.itextpdf

itext-asian

5.2.0

com.itextpdf.tool

xmlworker

5.5.11

org.freemarker

freemarker

2.3.28

2.上上面提到两个类的实现代码

package com.xxxxx.xxxx.file.config;

import java.io.BufferedWriter;

import java.io.ByteArrayInputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.StringWriter;

import java.nio.charset.Charset;

import java.util.Map;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import org.springframework.util.StringUtils;

import org.xhtmlrenderer.pdf.ITextRenderer;

import com.itextpdf.text.Document;

import com.itextpdf.text.Font;

import com.itextpdf.text.Image;

import com.itextpdf.text.Rectangle;

import com.itextpdf.text.pdf.BaseFont;

import com.itextpdf.text.pdf.PdfWriter;

import com.itextpdf.tool.xml.Pipeline;

import com.itextpdf.tool.xml.XMLWorker;

import com.itextpdf.tool.xml.XMLWorkerFontProvider;

import com.itextpdf.tool.xml.XMLWorkerHelper;

import com.itextpdf.tool.xml.html.CssAppliersImpl;

import com.itextpdf.tool.xml.html.Tags;

import com.itextpdf..FileRetrieve;

import com.itextpdf..ReadingProcessor;

import com.itextpdf.tool.xml.parser.XMLParser;

import com.itextpdf.tool.xml.pipeline.css.CSSResolver;

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.ImageProvider;

import freemarker.template.Configuration;

import freemarker.template.Template;

import lombok.extern.slf4j.Slf4j;

/**

*

* @描述:html/pdf生成器

*

* @作者:zhongjy

*

* @时间:7月15日 下午12:31:25

*/

@Slf4j

public class Generator {

/**

*

* @描述:生成html

*

* @返回:String

*

* @作者:zhongjy

*

* @时间:7月15日 下午12:33:58

*/

public static String htmlGenerate(String template, Map variables)

throws Exception {

Configuration config = FreemarkerConfiguration.getConfiguation();

Template tp = config.getTemplate(template);

StringWriter stringWriter = new StringWriter();

BufferedWriter writer = new BufferedWriter(stringWriter);

tp.process(variables, writer);

String htmlStr = stringWriter.toString();

writer.flush();

writer.close();

return htmlStr;

}

public static void pdfGenerate(String htmlStr, OutputStream out) throws Exception {

DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(htmlStr.getBytes()));

ITextRenderer renderer = new ITextRenderer();

renderer.setDocument(doc, null);

renderer.layout();

renderer.createPDF(out);

out.close();

}

/**

*

* @描述:生成pdf

*

* @返回:void

*

* @作者:zhongjy

*

* @时间:7月16日 上午10:59:11

*/

public static void pdfGeneratePlus(String htmlTemplate, Map dataMap,

String targetPdf, Rectangle pageSize, String header, boolean isFooter, File watermark)

throws Exception {

/**

* 根据freemarker模板生成html

*/

String htmlStr = htmlGenerate(htmlTemplate, dataMap);

final String charsetName = "UTF-8";

Document document = new Document(pageSize);

OutputStream out = new FileOutputStream(targetPdf);

/**

* 设置边距

*/

// document.setMargins(30, 30, 30, 30);

PdfWriter writer = PdfWriter.getInstance(document, out);

/**

* 添加页码

*/

PDFBuilder builder = new PDFBuilder(header, 10, pageSize, watermark, isFooter);

writer.setPageEvent(builder);

document.open();

/**

* html内容解析

*/

HtmlPipelineContext htmlContext =

new HtmlPipelineContext(new CssAppliersImpl(new XMLWorkerFontProvider() {

@Override

public Font getFont(String fontname, String encoding, float size, final int style) {

if (fontname == null) {

/**

* 操作系统需要有该字体, 没有则需要安装; 当然也可以将字体放到项目中, 再从项目中读取

*/

fontname = "STSong-Light";

encoding = "UniGB-UCS2-H";

}

Font font = null;

try {

font = new Font(BaseFont.createFont(fontname, encoding, BaseFont.NOT_EMBEDDED), size,

style);

} catch (Exception e) {

log.error("", e);

}

return font;

}

})) {

@Override

public HtmlPipelineContext clone() throws CloneNotSupportedException {

HtmlPipelineContext context = super.clone();

ImageProvider imageProvider = this.getImageProvider();

context.setImageProvider(imageProvider);

return context;

}

};

/**

* 图片解析

*/

htmlContext.setImageProvider(new AbstractImageProvider() {

String rootPath = "C:\\Users\\Administrator\\Desktop\\刘亦菲\\";

@Override

public String getImageRootPath() {

return rootPath;

}

@Override

public Image retrieve(String src) {

if (StringUtils.isEmpty(src)) {

return null;

}

try {

Image image = Image.getInstance(new File(rootPath, src).toURI().toString());

/**

* 图片显示位置

*/

image.setAbsolutePosition(400, 400);

if (image != null) {

store(src, image);

return image;

}

} catch (Exception e) {

log.error("", e);

}

return super.retrieve(src);

}

});

htmlContext.setAcceptUnknown(true).autoBookmark(true)

.setTagFactory(Tags.getHtmlTagProcessorFactory());

/**

* css解析

*/

CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);

cssResolver.setFileRetrieve(new FileRetrieve() {

@Override

public void processFromStream(InputStream in, ReadingProcessor processor) throws IOException {

try (InputStreamReader reader = new InputStreamReader(in, charsetName)) {

int i = -1;

while (-1 != (i = reader.read())) {

processor.process(i);

}

} catch (Throwable e) {

}

}

/**

* 解析href

*/

@Override

public void processFromHref(String href, ReadingProcessor processor) throws IOException {

InputStream is = new ByteArrayInputStream(href.getBytes());

try {

InputStreamReader reader = new InputStreamReader(is, charsetName);

int i = -1;

while (-1 != (i = reader.read())) {

processor.process(i);

}

} catch (Exception e) {

log.error("", e);

}

}

});

HtmlPipeline htmlPipeline =

new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer));

Pipeline> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);

XMLWorker worker = null;

worker = new XMLWorker(pipeline, true);

XMLParser parser = new XMLParser(true, worker, Charset.forName(charsetName));

try (InputStream inputStream = new ByteArrayInputStream(htmlStr.getBytes())) {

parser.parse(inputStream, Charset.forName(charsetName));

}

document.close();

}

}

package com.xxxxx.xxxx.file.config;

import java.io.File;

import com.itextpdf.text.BaseColor;

import com.itextpdf.text.Document;

import com.itextpdf.text.Element;

import com.itextpdf.text.Font;

import com.itextpdf.text.Image;

import com.itextpdf.text.PageSize;

import com.itextpdf.text.Phrase;

import com.itextpdf.text.Rectangle;

import com.itextpdf.text.pdf.BaseFont;

import com.itextpdf.text.pdf.ColumnText;

import com.itextpdf.text.pdf.PdfContentByte;

import com.itextpdf.text.pdf.PdfPageEventHelper;

import com.itextpdf.text.pdf.PdfTemplate;

import com.itextpdf.text.pdf.PdfWriter;

import com.xxxxx.xxxx.util.BaseUtil;

import lombok.extern.slf4j.Slf4j;

/**

*

* @描述:PDF生成水印和页眉页脚(页码).

*

* @作者:zhongjy

*

* @时间:7月15日 下午8:22:24

*/

@Slf4j

public class PDFBuilder extends PdfPageEventHelper {

/**

* 页眉

*/

public String header = "";

/**

* 文档字体大小,页脚页眉最好和文本大小一致

*/

public int presentFontSize = 10;

/**

* 文档页面大小,最好前面传入,否则默认为A4纸张

*/

public Rectangle pageSize = PageSize.A4;

/**

* 模板

*/

public PdfTemplate total;

/**

* 基础字体对象

*/

public BaseFont bf = null;

/**

* 利用基础字体生成的字体对象,一般用于生成中文文字

*/

public Font fontDetail = null;

/**

* 水印文件

*/

private File watermark = null;

/**

* 是否显示页脚页码信息

*/

private boolean isHeaderFooter = false;

public PDFBuilder() {

}

/**

*

* @param header

* @param presentFontSize

* @param pageSize

* @param watermark

*/

public PDFBuilder(String header, int presentFontSize, Rectangle pageSize, File watermark,

boolean isHeaderFooter) {

this.header = header;

this.presentFontSize = presentFontSize;

this.pageSize = pageSize;

this.watermark = watermark;

this.isHeaderFooter = isHeaderFooter;

}

public void setHeader(String header) {

this.header = header;

}

public void setPresentFontSize(int presentFontSize) {

this.presentFontSize = presentFontSize;

}

/**

* 文档打开时创建模板

*/

public void onOpenDocument(PdfWriter writer, Document document) {

/**

* 共 页 的矩形的长宽高

*/

total = writer.getDirectContent().createTemplate(50, 50);

}

/**

* 关闭每页的时候添加页眉页脚和水印

*/

public void onEndPage(PdfWriter writer, Document document) {

/**

* 添加分页

*/

if (isHeaderFooter) {

this.addPage(writer, document);

}

/**

* 添加水印

*/

if (watermark != null) {

this.addWatermark(writer);

}

}

/**

*

* @描述:分页

*

* @返回:void

*

* @作者:zhongjy

*

* @时间:7月15日 下午9:09:39

*/

public void addPage(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);

fontDetail.setColor(BaseColor.GRAY);

}

} catch (Exception e) {

log.error("", e);

}

/**

* 写入页眉

*/

ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT,

new Phrase(header, fontDetail), document.left(), document.top() + 20, 0);

/**

* 写入页脚(分页信息)

*/

int pageS = writer.getPageNumber();

String foot1 = "第 " + pageS + " 页 / 共";

Phrase footer = new Phrase(foot1, fontDetail);

/**

* 计算前半部分的foot1的长度,后面好定位最后一部分的'Y页'这俩字的x轴坐标,字体长度也要计算进去 = len

*/

float len = bf.getWidthPoint(foot1, presentFontSize);

/**

* 拿到当前的PdfContentByte

*/

PdfContentByte cb = writer.getDirectContent();

/**

* 写入页脚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() - 25, 0);

/**

* 写入页脚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() - 25);

}

/**

*

* @描述:添加水印

*

* @返回:void

*

* @作者:zhongjy

*

* @时间:7月15日 下午9:12:40

*/

public void addWatermark(PdfWriter writer) {

/**

* 水印图片

*/

Image image = null;

try {

image = Image.getInstance(BaseUtil.file2byte(watermark));

PdfContentByte content = writer.getDirectContentUnder();

content.beginText();

/**

* 开始写入水印

*/

image.setAbsolutePosition(300, 300);

content.addImage(image);

content.endText();

} catch (Exception e) {

log.error("", e);

}

}

/**

* 关闭文档时,替换模板,完成整个页眉页脚组件

*/

public void onCloseDocument(PdfWriter writer, Document document) {

if (isHeaderFooter) {

/**

* 最后一步了,就是关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size

*/

total.beginText();

/**

* 生成的模版的字体、颜色

*/

total.setFontAndSize(bf, presentFontSize);

total.setColorFill(BaseColor.GRAY);

String foot2 = " " + (writer.getPageNumber()) + " 页";

/**

* 模版显示的内容

*/

total.showText(foot2);

total.endText();

total.closePath();

}

}

}

最后附上HTML模板和调用方法

table {

width: 100%;

border-collapse: collapse;

border-style: solid;

border-width: 0.5px;

border-color: #000000;

}

table tr td {

border-width: 0.5px;

border-style: solid;

border-color: #000000;

padding: 9px 9px;

}

table thead tr th {

border-width: 0.5px;

border-style: solid;

border-color: #000000;

text-align: center;

padding: 9px 9px;

}

${title}

#list>

${d}#list>

#list>

import java.util.ArrayList;

import java.util.Arrays;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import com.itextpdf.text.PageSize;

import com.xxxxx.xxxx.file.config.Generator;

public class Test4 {

public static void main(String[] args) throws Exception {

Map mp = new HashMap();

try {

String outputFile = "C:\\Users\\Administrator\\Desktop\\test123\\abc1.pdf";// 生成后的路径

Map dataMap = new HashMap();

List titleList = Arrays.asList("属性1", "属性2", "属性3", "属性4", "属性5", "属性6", "属性7");

dataMap.put("titleList", titleList);

List> dataList = new ArrayList>();

for (int i = 0; i < 100; i++) {

dataList

.add(Arrays.asList("数据1_" + i, "数据2_" + i, "数据3_数据3_数据3_数据3_数据3_数据3_数据3_数据3_数据3_" + i,

"数据4_" + i, "数据5_" + i, "数据6_" + i, "数据7_" + i));

}

dataMap.put("dataList", dataList);

//File water = new File("C:\\Users\\zhongjy\\Desktop\\test123\\water.png");

Generator.pdfGeneratePlus("laytable/normal-teble.html", dataMap, outputFile, PageSize.A4, "", true, null);

mp.put("code", "200");

mp.put("url", outputFile);

} catch (Exception ex) {

ex.printStackTrace();

mp.put("code", "500");

}

}

}

运行结果图如下:

补充上面用到的方法

/**

*

* @描述:文件转byte[]

*

* @返回:byte[]

*

* @作者:zhongjy

*

* @时间:7月15日 下午10:19:18

*/

public static byte[] file2byte(File file) {

FileInputStream fileInputStream = null;

byte[] bFile = null;

try {

bFile = new byte[(int) file.length()];

fileInputStream = new FileInputStream(file);

fileInputStream.read(bFile);

} catch (Exception e) {

logger.error("", e);

} finally {

if (fileInputStream != null) {

try {

fileInputStream.close();

} catch (Exception e) {

logger.error("", e);

}

}

}

return bFile;

}

/**

*

* @描述:html报表模板配置

*

* @作者:zhongjy

*

* @时间:7月15日 下午12:25:59

*/

public class FreemarkerConfiguration {

private static Configuration config = null;

static {

config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);

config.setClassForTemplateLoading(FreemarkerConfiguration.class, "/report/");

}

public static Configuration getConfiguation() {

return config;

}

}

说明:/report/是springboot项目下freemarker的模板路径

————————————————

版权声明:本文为CSDN博主「zhong_jianyu」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:/zhong_jianyu/java/article/details/96147949

html pdf支持css%写法吗 flying-saucer-pdf终于完美解决了(中文问题 换行问题 分页 页眉页脚 水印) html+css控制pdf样式...

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。