java使用freemarker模板生成html再轉為pdf
1.freemarker模板生成html
添加Maven依賴
在pom.xml文件中添加以下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
創(chuàng)建Freemarker模板
新建一個HTML文件,例如table.ftl,然后在其中編寫HTML模板,包括表格的頭部、內容和尾部等部分。具體可以參考下面這個例子:
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<#list users as user>
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.age}</td>
</tr>
</#list>
</tbody>
</table>上面的模板中使用了Freemarker的指令語法來實現(xiàn)動態(tài)生成表格內容,包括使用<#list>標簽來遍歷用戶列表,并使用${}語法來輸出用戶信息。
創(chuàng)建Controller方法
在Controller中編寫一個方法來獲取用戶列表,然后渲染上面的模板并返回HTML內容。示例代碼如下:
@Controller
public class UserController {
@GetMapping("/users")
public String userList(Model model) {
List<User> users = new ArrayList<>();
users.add(new User(1, "Tom", 18));
users.add(new User(2, "Jerry", 20));
users.add(new User(3, "John", 22));
model.addAttribute("users", users);
return "table";
}
}
上面的方法使用@GetMapping注解來處理請求,然后創(chuàng)建一個用戶列表,并將其添加到模型中。最后返回table字符串,代表要使用的HTML模板文件。
運行項目
運行Spring Boot應用程序,然后使用瀏覽器訪問http://localhost:8080/users,即可看到動態(tài)生成的HTML表格。
注意:上面的例子僅供參考,實際應用中需要根據(jù)自己的需求進行修改擴展。
2.利用iText將生成的HTML轉換為PDF文件
理解了freemarker生成html的步驟以后,就可以利用iText把html生成pdf文件了。
1.編寫轉換代碼
添加Maven依賴
在pom.xml文件中添加以下依賴:
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!-- 支持css樣式渲染 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.0.9</version>
</dependency>
2.編寫轉換代碼
在SpringBoot應用程序中創(chuàng)建一個Service或者Controller類,然后編寫HTML轉PDF的代碼。
freeMarker轉換為html的方法:
public class HtmlGenerator {
??????? public static String generate(String template, Map<String, Object> variables) throws IOException, TemplateException, IOException {
Configuration config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
config.setClassForTemplateLoading(HtmlGenerator.class, "/filePath");
//讀取模板文件地址
config.setDefaultEncoding("UTF-8");
//獲取模板文件
Template tp = config.getTemplate(template);
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
tp.setEncoding("UTF-8");
//把map數(shù)據(jù)寫入
tp.process(variables, writer);
String htmlStr = stringWriter.toString();
writer.flush();
writer.close();
return htmlStr;
}
}/filePath為項目中的ftl文件相對路徑。
html生成pdf的方法:
public class PdfDocumentGenerator {
private static final Logger logger = LoggerFactory.getLogger(PdfDocumentGenerator.class);
/**
* Output a pdf to the specified outputstream
*
* @param htmlStr
* the htmlstr
* @param out
* the specified outputstream
* @throws Exception
*/
public static void generate(String htmlStr, OutputStream out)
throws Exception {
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
DocumentBuilder builder = df.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();
}
public static void generatePlus(String htmlStr, OutputStream out) throws IOException, DocumentException {
final String charsetName = "UTF-8";
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
document.setMargins(30, 30, 30, 30);
PdfWriter writer = PdfWriter.getInstance(document, out);
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 = getChineseFont();
}
return super.getFont(fontname, encoding, size,
style);
}
})) {
@Override
public HtmlPipelineContext clone()
throws CloneNotSupportedException {
HtmlPipelineContext context = super.clone();
try {
ImageProvider imageProvider = this.getImageProvider();
context.setImageProvider(imageProvider);
} catch (NoImageProviderException e) {
}
return context;
}
};
// 圖片解析
htmlContext.setImageProvider(new AbstractImageProvider() {
String rootPath = PdfDocumentGenerator.class.getResource("/").getPath();
@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 (Throwable e) {
e.printStackTrace();
}
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 = PdfDocumentGenerator.class.getResourceAsStream("/" + href);
try (InputStreamReader reader = new InputStreamReader(is,charsetName)) {
int i = -1;
while (-1 != (i = reader.read())) {
processor.process(i);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
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();
}
/**
* 獲取中文字體位置
* @return
*/
public static String getChineseFont() {
String chineseFont = null;
chineseFont = Object.class.getResource("/").getPath() + "font/simsun.ttc";
if(!new File(chineseFont).exists()){
throw new RuntimeException("字體文件不存在!"+chineseFont);
}
return chineseFont;
}
}運行測試方法:
public class Pdfdest {
public static void main(String[] args) throws Exception {
String outputFile = "d:/test1.pdf";
Map<String, Object> map = new HashMap<>();
map.put("XXX", "測試");
//生成工具,下面有代碼
String htmlStr = HtmlGenerator.generate("test.ftl", map);
//生成工具,下面有代碼
OutputStream out = new FileOutputStream(outputFile);
PdfDocumentGenerator.generatePlus(htmlStr,out);
}
}
準備一個test.ftl放到resource/filePath下,當然字體最好也放到resource/font下,運行時需要使用。
test.ftl的代碼如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"></meta>
<style>
@page {
@top-center { content: element(header) }
}
@page {
@bottom-center { content: element(footer) }
}
.apply {
margin: 0 auto;
padding: 0 30px;
}
.title {
margin-top: 40px ;
text-align: center;
font-weight: bold;
//字體需要和后臺對應上
font-family: SimSun;
font-weight: bold;
font-size: 20px;
color: #333333;
letter-spacing: 0;
}
.table {
border-collapse: collapse;
width: 100%;
margin-top: 30px;
font-family: SimSun;
font-size: 14px;
color: #111111;
letter-spacing: 0.54px;
}
.label {
background-color: #E6E6E6;
width: 20%;
}
.normaltd {
padding: 10px 0;
}
.maxtd {
height: 250px;
}
.value {
width: 30%;
padding-left: 10px;
}
.apply {
margin: 0 auto;
padding: 0 30px;
}
.title {
margin-top: 40px ;
text-align: center;
font-weight: bold;
//字體需要和后臺對應上
font-family: SimSun;
font-weight: bold;
font-size: 20px;
color: #333333;
letter-spacing: 0;
}
.table {
width: 100%;
margin-top: 30px;
font-family: SimSun;
font-size: 14px;
color: #111111;
letter-spacing: 0.54px;
}
.label {
background-color: #E6E6E6;
width: 20%;
}
.normaltd {
padding: 10px 0;
}
.maxtd {
height: 250px;
}
.value {
width: 30%;
padding-left: 10px;
}
tr {
page-break-inside: avoid;
page-break-after: auto;
}
</style>
</head>
<body style="font-family: SimSun">
<div class="apply">
<p class="title">申請單</p>
<table border="1" cellspacing="0" class="table">
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">XXX</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr >
<td valign="middle" colspan="1" class="label maxtd" align="center">XXX</td>
<td valign="middle" colspan="3" class="maxtd value">${XXX}</td>
</tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXX</td>
<td colspan="3" class="normaltd value">${XXX}</td>
</tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXX</td>
<td colspan="3" class="normaltd value">${XXX}</td>
</tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXX</td>
<td colspan="3" class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">XXX</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr >
<td valign="middle" colspan="1" class="label maxtd" align="center">XXX</td>
<td valign="middle" colspan="3" class="maxtd value">${XXX}</td>
</tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXX</td>
<td colspan="3" class="normaltd value">${XXX}</td>
</tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXX</td>
<td colspan="3" class="normaltd value">${XXX}</td>
</tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXX</td>
<td colspan="3" class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">XXX</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
<tr>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
<td class="label normaltd" align="center">XXX</td>
<td class="normaltd value">${XXX}</td>
</tr>
</table>
</div>
</body>
</html>
執(zhí)行就可以看到生成的pdf文件了,文件路徑在d:/test1.pdf。
到此這篇關于java使用freemarker模板生成html再轉為pdf的文章就介紹到這了,更多相關java freemarker生成html內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用位運算、值交換等方式反轉java字符串的多種方法(四種方法)
這篇文章主要介紹了使用位運算、值交換等方式反轉java字符串,本文通過四種方式給大家講解,給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
Springboot實現(xiàn)全局自定義異常的方法詳解
這篇文章主要介紹了Springboot實現(xiàn)全局自定義異常的方法詳解,SpringBoot的項目已經(jīng)對有一定的異常處理了,但是對于我們開發(fā)者而言可能就不太合適了,因此我們需要對這些異常進行統(tǒng)一的捕獲并處理,需要的朋友可以參考下2023-11-11

