freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head lang="en">
<meta http-equiv="content-type" content="text/html;charset=utf-8"></meta>
<title>軟件包隱患掃描報告</title>
<style>
.template_ftl {
width: 100%;
height: 100%;
padding: 30px 25px;
}
.template_box {
width: 600px;
height: 842px;
box-sizing: border-box;
margin: 0 auto;
}
.template_ftl .template_header {
width: 100%;
height: 50px;
box-sizing: border-box;
color: #0D122B;
}
.template_ftl .template_header > i {
display: inline-block;
width: 24px;
height: 28px;
background: url("stack.png") no-repeat center center;
}
.template_ftl .template_header > h2 {
display: inline-block;
font-size: 17px;
}
.template_ftl .template_header > div {
float: right;
color: #B7C1CF;
font-size: 9px;
line-height: 50px;
}
.template_ftl .template_header > div > i {
display: inline-block;
width: 9px;
height: 9px;
background: url("Calendar.png") no-repeat center center;
}
.template_ftl .template_description {
width: 100%;
margin-top: 10px;
}
.template_ftl .template_title {
width: 100%;
height: 18px;
line-height: 18px;
color: #00AEA1;
font-size: 12px;
}
.template_ftl .template_title > i {
display: inline-block;
width: 14px;
height: 14px;
margin-right: 10px;
background: url("Layers.png") no-repeat center center;
}
.template_ftl .template_info {
list-style: none;
padding-left: 30px;
}
.template_ftl .template_info label {
display: inline-block;
width: 80px;
margin-right: 30px;
color: #646464;
font-size: 10px;
vertical-align: middle;
}
.template_ftl .template_info span {
color: #0D122B;
font-size: 10px;
vertical-align: middle;
}
.template_ftl .template_info li {
margin-bottom: 5px;
}
.template_ftl .template_title .shield {
width: 14px;
height: 14px;
background: url("Shield.png") no-repeat center center;
}
.template_ftl .template_title .screen {
width: 14px;
height: 14px;
background: url("Screen.png") no-repeat center center;
}
.template_ftl .template_network {
list-style: none;
padding-left: 30px;
margin-top: 10px;
}
.template_ftl .template_network .network_level b {
color: #0D122B;
font-size: 12px;
}
.template_ftl .template_network .network_level span {
color: #E74C3C;
font-size: 12px;
}
.template_ftl .template_network .network_level p {
margin: 0;
color: #646464;
font-size: 10px;
}
.template_ftl .template_network .network_content:before {
content: '● ';
color: #00AEA1;
}
.template_ftl .template_network .network_content {
color: #3E4550;
font-size: 10px;
margin-top: 10px;
}
.online_info {
width: 100%;
}
.online_info .online_list {
width: 100%;
box-sizing: border-box;
list-style: none;
padding-left: 30px;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
margin: 0;
color:#3E4550;
font-size: 10px;
}
.online_info .online_list:nth-child(2n){
background-color: #EFEFEF;
}
.online_info .online_list > td {
width: 90px;
line-height: 25px;
text-align: center;
}
.online_info .online_list > td:first-child {
text-align: center;
}
</style>
</head>
<body>
<div class="template_ftl" style="font-family:SimSun;">
<div class="template_box">
<div class="template_header">
<i></i>
<span style="font-size:24px;font-weight:1px bold;">軟件包掃描報告</span>
<div>
<i></i>
<span>
<#if (currentDate)??>
${currentDate}
</#if>
</span>
</div>
</div>
<div class="template_description">
<div class="template_title">
<i></i>
<span>掃描概要信息</span>
</div>
<ul class="template_info">
<li>
<label>掃描產品名稱</label>
<span>
<#if (softScanInfo.productName)??>
${softScanInfo.productName}
</#if>
</span>
</li>
<li>
<label>文件路徑</label>
<span>
<#if (softScanInfo.scanFilePath)??>
${softScanInfo.scanFilePath}
</#if>
</span>
</li>
<li>
<label>掃描開始時間</label>
<span>
<#if (softScanInfo.scanBegintime)??>
${(softScanInfo.scanBegintime)?string("yyyy年MM月dd日 HH:mm:ss")}
</#if>
</span>
</li>
<li>
<label>掃描結束時間</label>
<span>
<#if (softScanInfo.scanEndtime)??>
${(softScanInfo.scanEndtime)?string("yyyy年MM月dd日 HH:mm:ss")}
</#if>
</span>
</li>
</ul>
</div>
<div class="template_description">
<div class="template_title">
<i class="shield"></i>
<span>軟件包安全信息</span>
</div>
<ul class="template_network">
<li class="network_level">
<span style="font-size:13px;color:black;">軟件包風險等級為 </span>
<#if (softScanInfo.scanResult)??>
<#if (softScanInfo.scanResult == '高')>
<span style="color:red;">${softScanInfo.scanResult}</span>
<#elseif (softScanInfo.scanResult == '中')>
<span style="color:#FF9900;">${softScanInfo.scanResult}</span>
<#elseif (softScanInfo.scanResult == '低')>
<span style="color:#0000FF;">${softScanInfo.scanResult}</span>
<#else>
<span style="color:#00cc00;">安全</span>
</#if>
<#else>
<span style="color:#00cc00;">安全</span>
</#if>
<p style="margin-top:8px;">本次共掃描1個軟件包,其中包含<#if recordList?exists>${recordList?size} <#else>0</#if>款軟件</p>
</li>
<li class="network_content">
<span>
風險等級為<span style="color:red;">高</span>的軟件數為
<span style="color:red;"><#if (highRiskRecord.appScanResult)??>${highRiskRecord.appScanResult}</span></#if>
</span>
</li>
<li class="network_content">
<span>
風險等級為<span style="color:#FF9900;">中</span>的軟件數為
<span style="color:#FF9900;"><#if (middleRiskRecord.appScanResult)??>${middleRiskRecord.appScanResult}</span></#if>
</span>
</li>
<li class="network_content">
<span>
風險等級為<span style="color:#0000FF;">低</span>的軟件數為
<span style="color:#0000FF;"><#if (lowRiskRecord.appScanResult)??>${lowRiskRecord.appScanResult}</span></#if>
</span>
</li>
<li class="network_content">
<span>
風險等級為<span style="color:#00cc00;">安全</span>的軟件數為
<span style="color:#00cc00;"><#if (noneRiskRecord.appScanResult)??>${noneRiskRecord.appScanResult}</span></#if>
</span>
</li>
</ul>
</div>
<div class="template_description">
<div class="template_title">
<i class="screen"></i>
<span>掃描軟件包概要信息</span>
</div>
<div class="online_info" >
<table class="online_list" style="word-break: break-all; word-wrap: break-word;">
<tr class="online_list" style="border-bottom: 1px solid #B7C1CF;color: #94949;">
<td >軟件名稱</td>
<td style="width:120px;">版本號</td>
<td style="width:150px;">軟件包名稱</td>
<td style="width:40px;">安全風險</td>
<td style="width:150px;">安全描述事件</td>
<td style="width:30px;">通過</td>
</tr>
<#if recordList?exists>
<#list recordList as record>
<tr class="online_list" >
<td>
<#if (record.appName)??>
${record.appName}
</#if>
</td>
<td style="width:120px;">
<#if (record.appVersion)??>
${record.appVersion}
</#if>
</td>
<td style="width:150px;">
<#if (record.appFileName)??>
${record.appFileName}
</#if>
</td>
<td style="width:30px;">
<#if (record.riskRating)??>
<#if (record.riskRating == '無')>
安全
<#else>
${record.riskRating}
</#if>
<#else>
安全
</#if>
</td>
<td style="width:150px;">
<#if (record.riskDescription)??>
${record.riskDescription}
<#else>
無
</#if>
</td>
<td style="width:20px;">
<#if (record.appScanResult)??>
<#if (record.appScanResult = 1)>
<i style="color: #00AEA1">√</i>
<#else>
<i style="color: #E74C3C">×</i>
</#if>
<#else>
<i style="color: #00AEA1">√</i>
</#if>
</td>
</tr>
</#list>
</#if>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
2. 獲取模板,并將所獲取的數據加載生成html文件
public String getHtmlString (final Object data, final String templateDirectory, final String templateName, final String encoding) {
log.info("FreeMarkerUtil!");
String html = null;
StringWriter stringWriter = null;
BufferedWriter writer = null;
try {
//創建一個負責管理FreeMarker模板的Configuration實例
final Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//指定FreeMarker模板文件位置
// configuration.setDirectoryForTemplateLoading(new File(templateDirectory));
//設置模板的編碼格式
configuration.setEncoding(Locale.CHINA, encoding);
//獲取模板
configuration.setClassForTemplateLoading(this.getClass(), "/templates");
final Template template = configuration.getTemplate(templateName, encoding);
stringWriter = new StringWriter();
writer = new BufferedWriter(stringWriter);
//將數據輸出到html中
template.process(data, writer);
writer.flush();
html = stringWriter.toString();
} catch (Exception e) {
log.error("failed to get html String!", e);
} finally {
if (stringWriter !=null) {
try {
stringWriter.close();
} catch (IOException e) {
log.error("failed to close writer!", e);
}
}
if (writer !=null) {
try {
writer.close();
} catch (IOException e) {
log.error("failed to close writer!", e);
}
}
}
log.info("getHtmlString() exit!");
return html;
}
2. 生成PDF文件
final File file = new File(reportPath);
try {
if (!file.exists()) {
file.createNewFile();
}
final String chineseFont = getChineseFont();//獲取字體文件路徑
log.info("chineseFont=" + chineseFont);
createPDF(new FileOutputStream(file), chineseFont, html);
}
public void createPDF(final OutputStream outputStream, final String fontPath, final String html) {
final ITextRenderer renderer = new ITextRenderer();
// 設置字體樣式
try {
renderer.getFontResolver().addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//這里的字體必須和ftl模板中的字體一致,否則中文會丟失
// 把html代碼傳入渲染器中
renderer.setDocumentFromString(html);
/*
* 解決圖片的相對路徑問題,必須在設置document后再設置圖片路徑,不然不起作用,
* 如果使用絕對路徑依然有問題,可以在路徑前面加"file:/"
*/
// renderer.getSharedContext().setBaseURL("file:/C:/Users/HYH/Desktop/templateftp/");
renderer.getSharedContext().setBaseURL("file:" + getPictureDirctory());
log.info("images directory path: file:" + getPictureDirctory());
renderer.layout();
renderer.createPDF(outputStream, false);
renderer.finishPDF();
outputStream.flush();
} catch (final Exception e) {
log.error("failed to create pdf!", e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (final IOException e) {
log.error("failed to close outputStream!", e);
}
}
}
log.info("create pdf completely!");
}
其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件;
//獲取圖片目錄位置
private String getPictureDirctory () {
final File fileDir = new File("System.getProperty("user.dir") + "/images/"");
if (!fileDir.exists()) {
fileDir.mkdirs();
final InputStream calendarStream = getClass().getClassLoader().getResourceAsStream("images/Calendar.png");
final InputStream deleteStream = getClass().getClassLoader().getResourceAsStream("images/delete.png");
final InputStream layersStream = getClass().getClassLoader().getResourceAsStream("images/Layers.png");
final InputStream saveStream = getClass().getClassLoader().getResourceAsStream("images/save.png");
final InputStream screenStream = getClass().getClassLoader().getResourceAsStream("images/Screen.png");
final InputStream shieldStream = getClass().getClassLoader().getResourceAsStream("images/Shield.png");
final InputStream stackStream = getClass().getClassLoader().getResourceAsStream("images/stack.png");
final File calendarFile = new File(IMAGES_TEMP_PATH + "Calendar.png");
final File deleteFile = new File(IMAGES_TEMP_PATH + "delete.png");
final File layersFile = new File(IMAGES_TEMP_PATH + "Layers.png");
final File saveFile = new File(IMAGES_TEMP_PATH + "save.png");
final File screenFile = new File(IMAGES_TEMP_PATH + "Screen.png");
final File shieldFile = new File(IMAGES_TEMP_PATH + "Shield.png");
final File stackFile = new File(IMAGES_TEMP_PATH + "stack.png");
try {
FileUtils.copyInputStreamToFile(calendarStream, calendarFile);
FileUtils.copyInputStreamToFile(deleteStream, deleteFile);
FileUtils.copyInputStreamToFile(layersStream, layersFile);
FileUtils.copyInputStreamToFile(saveStream, saveFile);
FileUtils.copyInputStreamToFile(screenStream, screenFile);
FileUtils.copyInputStreamToFile(shieldStream, shieldFile);
FileUtils.copyInputStreamToFile(stackStream, stackFile);
} catch (IOException e) {
log.error("圖片文件復制失敗!", e);
}
}
return IMAGES_TEMP_PATH;
}
/**
* 獲取中文字體位置
* @return
*/
private String getChineseFont() {
final InputStream stream = getClass().getClassLoader().getResourceAsStream("fonts/simsun.ttf");
final File fileDir = new File("System.getProperty("user.dir") + "/fonts/"");
if (!fileDir.exists()) {
fileDir.mkdirs();
}
final String fontsPath = FONTS_TEMP_PATH + "simsun.ttf";
final File targetFile = new File(fontsPath);
try {
FileUtils.copyInputStreamToFile(stream, targetFile);
} catch (final IOException e) {
log.error("字體文件復制失敗!", e);
}
return fontsPath;
}
還有一個問題至今沒有解決,就是關于生成PDF文件后自動換行的問題,參考了網上大多數的作法,都是修改模板樣式,生成的html確實是可以換行,但是對PDF無效。最后想出了一個比較笨也不是很合適的方法,在后臺先給它在某處先換好行,然后再生成PDF,雖然比較笨,也存在問題,但是確實解了燃眉之急。
/**
* 向字符串source中每隔sep個插入一個replace
* @param source
* @param sep
* @param replace
* @return
*/
public String changeLine(final String source, final int sep, final String replace) {
if (source == null) {
return null;
}
final StringBuffer buffer = new StringBuffer(source);
for (int i = sep; i < source.length(); i+=(sep + 1)) {
buffer.insert(i, replace);
}
return buffer.toString();
}
最終結果
如果有大神知道如何正確實現自動換行,還請不吝賜教。
智能推薦
JavaWeb 利用 freemarker 根據模板生成word并下載
1、用到的jar包 用到的jar包為freemarker-2.3.18.jar 2、word模板文件處理 首先將確定好的word模板文件另存為xml格式,用notepad++打開,將要替換的部分用${**}替換掉,如下圖所示: 如果有表格需要遍歷顯示的,找到對應需要遍歷的行,在外層用<#list ? as row ></#list>進行包圍,遍歷的行用 ${row.?}來替...
使用FreeMarker生成模板代碼
在平時開發過程中,一般都是需要Dao層,Service層,而每一層當中都需要自己編寫一部分常用代碼,例如在Dao(基于Mybatis框架)層中編寫返回值為List的方法用于查詢數據庫中數據,或者是add方法對數據庫執行插入操作等一系列的方法,而且我們發現再換做另外一張表的時候也是這種操作,list,add。。。。。。 那么什么樣的方式可以減少這種重復的工作呢? FreeMarker可以做到,Fre...
freemarker自動生成代碼模板
http://download.csdn.net/download/dwangc/9895462 最近需求中,根據數據庫表的信息生成PO,DAO,還有sql語句,如果手寫很是重復繁瑣。然后又要用到mybaties,所以給我的任務就是自動生成PO類,DAO類和sql的xml文件。 首先分析了給我的最簡單的需求樣例, 1. 一個實體類對應數據庫表的字段。 2. 一個DAO類,包括簡單的有null in...
用freemarker生成word模板
需求: 給文書統一生成一個搞頭文件,文件內容基本是一樣的。 用freemarker生成docx文檔 一、生成一個docx結尾的word模板,然后把文檔的后綴docx改成zip,zip里會有生成很多文件。如下圖: 然后打開word文件夾,如下圖: 把document.xml拿出來,把后綴xml改成ftl,然后把里面的內容(在線格式化xml)格式化一下,把一些可變的參數做成變量。 二、在resourc...
猜你喜歡
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...