文章目录
- xml配置
- dtd文件的编码及说明
- dtd文件
- dtd文件说明
- xml编码及说明
- NewFile.xml
- test-include.xml
- xml说明
- xml数据模型
- BaseMapBean
- Tablepage
- Table
- Title
- Tr
- Td
- 数据装配
- 一个调用的示例
- 待改进的地方
使用itext将xml转换为pdf(一)主要是一个探索的过程,所以使用Java project来立项。现在经过将近一个月的整理和整合,在web项目中已经渐近成熟。再总结一下。
基本上分为三个模块
- xml的配置
- xml数据结构
- xml解析与数据装配
类似于mvc结构,即xml配置为前端展现view,数据装配为controller,xml对应的数据模型为model。
用到了dom4j的相关jar,还有就是jaxb2,这个主要是借鉴网上的。
xml配置
主要是dtd约束及xml的编写
dtd文件的编码及说明
dtd的主要作用是用来约束xml的编写规范。
dtd文件
<!ELEMENT tablepage (title,tables,include)>
<!ELEMENT tables (table,include)>
<!ELEMENT table (trs,include)>
<!ELEMENT trs (tr)>
<!ELEMENT tr (tds)>
<!ELEMENT tds (td)>
<!ELEMENT td (text)>
<!ATTLIST include file CDATA #IMPLIED>
<!ATTLIST title align CDATA "center"
v-align CDATA "middle"
font-family CDATA #IMPLIED>
<!ATTLIST table entity CDATA #REQUIRED
rows CDATA #IMPLIED
font CDATA "微软雅黑"
rotate CDATA "false">
<!ATTLIST tr foreach CDATA "false"
entities CDATA "">
<!ATTLIST td align CDATA "center"
v-align CDATA "middle"
rowspan CDATA #IMPLIED
colspan CDATA "微软雅黑">
dtd文件说明
xml-config中配置的是每个pdf对应的xml,下边是参数的dtd文档说明
tablepage是xml的root节点,有且只有一个
tablepage的子节点为title和tables
<!ELEMENT tablepage (title,tables)>
//tablepage下可以有多个table,多个table用tables包裹,其子节点用有且只有一个trs
<!ELEMENT tables (table)>
//table子节点能且只能是trs
<!ELEMENT table (trs)>
//table下可以有多个tr,多个tr用trs包裹
<!ELEMENT trs (tr)>
//tr子节点能且只能是tds
<!ELEMENT tr (tds)>
//tds子节点能且只能是td
<!ELEMENT tds (td)>
<!ELEMENT td (text)>
//包含文件,其中file只得是xml-config下的文件路径,不包含xml-config,但要包含.xml后缀,file不能为空.
//被包含文件不能有根节点tablepage
<!ATTLIST include file CDATA #IMPLIED>
//下边是每个标签的熟悉,属性名可以参考html,如有不懂之处,请上网搜索
<!ATTLIST title align CDATA "center"
v-align CDATA "middle"
font-family CDATA #IMPLIED>
<!ATTLIST table entity CDATA #REQUIRED
rows CDATA #IMPLIED
font CDATA "微软雅黑"
//rotate,false:正常A4,true:横向A4
rotate CDATA "false">
<!ATTLIST tr foreach CDATA "false"
//foreach 用来指定是否是一个循环
//配合下边的entities,entities的数据结构为List<Map<String,Object>>
entities CDATA "">
<!ATTLIST td align CDATA "center"
v-align CDATA "middle"
rowspan CDATA #IMPLIED
//四个边框的宽度,其顺序按照上、右、下、左的顺序设置
//若只有一个值则为上、右、下、左
//若两个值则前一个值为上下,后一个值为左右
border-width CDATA "1 1 1 1"
colspan CDATA "微软雅黑">
xml编码及说明
NewFile.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tablepage PUBLIC "tablepage" "http://localhost:8080/dtd/tablepage.dtd">
<tablepage file-name="">
<!-- <title align="center" v-align="middle" font-family="STSong-Light"> -->
<!-- <text>测试标题</text> -->
<!-- </title> -->
<tables>
<table cols="12" font-family="STSong-Light" entity="cpafInfo" rotate="false">
<title align="center" v-align="middle" font-family="STSong-Light">
<text>事务所基本信息</text>
</title>
<trs>
<tr>
<tds>
<td colspan="12" align="center" v-align="middle" border-width="1 1 1 1">
<text>基本情况</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle" border-width="2 2">
<text>名称</text>
</td>
<td colspan="10" align="left" v-align="middle">
<text>{{cpafName}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle" border-width="3">
<text>所属行政区划</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{divisionProvince}}</text>
</td>
<td colspan="2" align="left" v-align="middle" border-width="0 0">
<text>组织形式</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{orgForm}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>执业许可批准日期</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{approDate}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>统一社会信用代码</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{regisCno}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>执业许可批准文号</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{rna}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>执业证书编号</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{cpafCno}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" rowspan="2" align="left" v-align="middle">
<text>注册资本(出资总额)(单位:万元)</text>
</td>
<td colspan="4" rowspan="2" align="left" v-align="middle">
<text>{{totalInves}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>分所数量</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{cpafbNum}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>国际网络名称(如有)</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{internetName}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>经营场所</text>
</td>
<td colspan="10" align="left" v-align="middle">
<text>{{officeLocation}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>通讯地址</text>
</td>
<td colspan="10" align="left" v-align="middle">
<text>{{address}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>报备业务联系人</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{reporter}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>电子邮箱</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{email}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>报备业务联系电话</text>
</td>
<td colspan="10" align="left" v-align="middle">
<text>{{phone}}</text>
</td>
</tds>
</tr>
</trs>
</table>
<include file="test-include.xml"/>
</tables>
</tablepage>
test-include.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tablepage PUBLIC "tablepage" "http://localhost:8080/dtd/tablepage.dtd">
<table cols="12" font-family="STSong-Light" entity="cpafInfo"
rotate="true">
<title align="center" v-align="middle" font-family="STSong-Light">
<text>测试横向页面</text>
</title>
<trs>
<tr>
<tds>
<td colspan="12" align="center" v-align="middle">
<text>基本情况</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>名称</text>
</td>
<td colspan="10" align="left" v-align="middle">
<text>{{cpafName}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>所属行政区划</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{divisionProvince}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>组织形式</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{orgForm}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>执业许可批准日期</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{approDate}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>统一社会信用代码</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{regisCno}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>执业许可批准文号</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{rna}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>执业证书编号</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{cpafCno}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" rowspan="2" align="left" v-align="middle">
<text>注册资本(出资总额)(单位:万元)</text>
</td>
<td colspan="4" rowspan="2" align="left" v-align="middle">
<text>{{totalInves}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>分所数量</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{cpafbNum}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>国际网络名称(如有)</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{internetName}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>经营场所</text>
</td>
<td colspan="10" align="left" v-align="middle">
<text>{{officeLocation}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>通讯地址</text>
</td>
<td colspan="10" align="left" v-align="middle">
<text>{{address}}</text>
</td>
</tds>
</tr>
<tr foreach="true" entities="">
<tds>
<td colspan="2" align="left" v-align="middle">
<text>报备业务联系人</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{reporter}}</text>
</td>
<td colspan="2" align="left" v-align="middle">
<text>电子邮箱</text>
</td>
<td colspan="4" align="left" v-align="middle">
<text>{{email}}</text>
</td>
</tds>
</tr>
<tr>
<tds>
<td colspan="2" align="left" v-align="middle">
<text>报备业务联系电话</text>
</td>
<td colspan="10" align="left" v-align="middle" border-width='0 0.5 0 0'>
<text>{{phone}}</text>
</td>
</tds>
</tr>
</trs>
</table>
xml说明
这里的主要问题是
<!DOCTYPE tablepage PUBLIC "tablepage" "http://localhost:8080/dtd/tablepage.dtd">
在使用itext将xml转换为pdf(一)中使用SYSTEM发现,需要在两个位置配置tablepage.dtd的位置,在web中也一样需要在两个位置,但位置比较奇怪,而且SYSTEM后边也不能简单的写"tablepage.dtd"就可以了,java项目xml使用自定义dtd位置问题中有说明。
后来在同事的探索中,更新了使用方法,即使用PUBLIC方式,但这中方式就不能使用本地文件,需要将文件放在网络可以访问的位置,虽然比较成功,但是在开发中还是不够完美:首先就是必须能访问到,这就意味着如果是localhost的话,必须启动服务,而且每个人的端口不同,需要每个人自己去改动;再就是每次上线都要做改动,比较麻烦。不过暂时这么做也比较合理。就这样先放着,后边有好的解决方法再补充。
xml数据模型
这个就是java中对应xml的bean了
因为后边数据装配中需要对数据的格式做一些规范,所以bean都需要继承一个基类。
BaseMapBean
package com.ufgov.util.pdf.entity;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ufgov.util.StringUtil;
/**
* pdf bean基类. 将需要转换为Map的bean继承自此抽象类 如果所需要的类型在toMap方法中不存在,可以添加;
* 如果基类的toMap方法实在不能满足您的需求,您可以重写此方法
*
* @author lihh
*
*/
public abstract class BaseMapBean {
/**
* 转化为Map
*
* @param digit
* BigDecimal的转换精度
* @return
*/
public List<Map<String, Object>> toMap(Integer digit) {
if (digit == null || digit < 0) {
digit = 2;
}
Map<String, Object> map = new HashMap<String, Object>();
try {
// 内省是 Java 语言对 Bean 类属性、事件的一种缺省处理方法。例如类 PersonBean中有属性 name,
// 那我们可以通过
// getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name
// 属性,这就是默认的规则。 Java
// 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API
// 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些
// API 存放于包 java.beans 中。注意:
// PersonBean中属性mN的getter/setter方法必须满足javaBean命名规范,即getmN,不能写作getMN,否则转换失败。
BeanInfo beanInfo = Introspector.getBeanInfo(this.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : propertyDescriptors) {
String key = property.getName();
// 过滤class属性
if (!key.equals("class")) {
// 得到property对应的getter方法
Method getter = property.getReadMethod();
Object value = getter.invoke(this);
if (value != null) {
if (value instanceof BigDecimal) {
// TODO:这个地方是不是要把精度做成可选?
value = StringUtil.numberFormat((BigDecimal) value, digit);
} else {
System.out.println(value.getClass().toString());
}
}
map.put(key, value);
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("transBean2Map Error " + e);
return null;
}
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
list.add(map);
return list;
}
}
其他实体
Tablepage
package com.ufgov.util.pdf.entity;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* 对应xml中的tablepage标签
*
* @author lihh
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "tablepage")
@XmlType(propOrder = { "title", "tableList" })
public class Tablepage {
@XmlAttribute(name="file-name")
private String fileName;
@XmlElement(name = "title")
private Title title;
@XmlElementWrapper(name = "tables")
@XmlElement(name = "table")
private List<Table> tableList;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Title getTitle() {
return title;
}
public void setTitle(Title title) {
this.title = title;
}
public List<Table> getTableList() {
return tableList;
}
public void setTableList(List<Table> tableList) {
this.tableList = tableList;
}
}
Table
package com.ufgov.util.pdf.entity;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlType;
/**
* 对应xml中的table标签
*
* @author lihh
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "table", propOrder = { "title", "trList" })
public class Table {
@XmlElement(name = "title")
private Title title;
@XmlElementWrapper(name = "trs")
@XmlElement(name = "tr")
private List<Tr> trList;
@XmlAttribute(name = "cols")
private String cols;
@XmlAttribute(name = "font-family")
private String fontFamily;
@XmlAttribute(name = "entity")
private String entity;
@XmlAttribute(name = "rotate")
private String rotate;
public String getRotate() {
return rotate;
}
public void setRotate(String rotate) {
this.rotate = rotate;
}
public Title getTitle() {
return title;
}
public void setTitle(Title title) {
this.title = title;
}
public List<Tr> getTrList() {
return trList;
}
public void setTrList(List<Tr> trList) {
this.trList = trList;
}
public String getCols() {
return cols;
}
public void setCols(String cols) {
this.cols = cols;
}
public String getFontFamily() {
return fontFamily;
}
public void setFontFamily(String fontFamily) {
this.fontFamily = fontFamily;
}
public String getEntity() {
return entity;
}
public void setEntity(String entity) {
this.entity = entity;
}
}
Title
package com.ufgov.util.pdf.entity;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "text" })
public class Title {
@XmlElement(name = "text", required = true)
private String text;
@XmlAttribute(name = "align")
private String align;
@XmlAttribute(name = "v-align")
private String vAlign;
@XmlAttribute(name = "font-family")
private String fontFamily;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getAlign() {
return align;
}
public void setAlign(String align) {
this.align = align;
}
public String getvAlign() {
return vAlign;
}
public void setvAlign(String vAlign) {
this.vAlign = vAlign;
}
public String getFontFamily() {
return fontFamily;
}
public void setFontFamily(String fontFamily) {
this.fontFamily = fontFamily;
}
}
Tr
package com.ufgov.util.pdf.entity;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "tr",propOrder= {"tdList"})
public class Tr {
@XmlElementWrapper(name = "tds")
@XmlElement(name = "td")
private List<Td> tdList;
@XmlAttribute(name="foreach")
private String foreach;
@XmlAttribute(name = "entities")
private String entities;
public String getEntities() {
return entities;
}
public void setEntities(String entities) {
this.entities = entities;
}
public String getForeach() {
return foreach;
}
public void setForeach(String foreach) {
this.foreach = foreach;
}
public List<Td> getTdList() {
return tdList;
}
public void setTdList(List<Td> tdList) {
this.tdList = tdList;
}
}
Td
package com.ufgov.util.pdf.entity;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "td", propOrder = { "text" })
public class Td {
@XmlAttribute(name = "rowspan")
private String rowspan;
@XmlAttribute(name = "border-width")
private String borderWidth;
@XmlAttribute(name = "colspan")
private String colspan;
@XmlAttribute(name = "align")
private String align;
@XmlAttribute(name = "v-align")
private String vAlign;
@XmlElement(name = "text")
private String text;
public String getBorderWidth() {
return borderWidth;
}
public void setBorderWidth(String borderWidth) {
this.borderWidth = borderWidth;
}
public String getSText() {
return text;
}
public void setString(String text) {
this.text = text;
}
public String getRowspan() {
return rowspan == null ? "1" : rowspan;
}
public void setRowspan(String rowspan) {
this.rowspan = rowspan;
}
public String getColspan() {
return colspan == null ? "1" : colspan;
}
public void setColspan(String colspan) {
this.colspan = colspan;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getAlign() {
return align;
}
public void setAlign(String align) {
this.align = align;
}
public String getvAlign() {
return vAlign;
}
public void setvAlign(String vAlign) {
this.vAlign = vAlign;
};
}
至此数据模型完毕
数据装配
这个就是纽带了
首先就是Jaxb2工具类,其中的converyToJavaBean就是xml转换为bean的核心
package com.ufgov.util.pdf.util;
import java.io.File;
import java.io.StringReader;
import java.util.List;
import javax.xml.bind.JAXBContext;
import org.apache.commons.collections.CollectionUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* Jaxb2工具类
* xml转换成JavaBean核心方法
*
* @author lihhz
* @create 2018-4-9 下午2:40:14
*/
public class JaxbUtil {
/**
* xml转换成JavaBean
*
* @param xml
* @param c
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T converyToJavaBean(String xmlPath, Class<T> clazz) {
try {
// 根据指定的路径创建file对象
File xmlFile = new File(JaxbUtil.class.getClassLoader().getResource("xml-config/" + xmlPath + ".xml").getFile());
if (!xmlFile.exists()) {
throw new Exception("文件xml-config/" + xmlPath + ".xml不存在!!!");
}
Document xmlDocument = (new SAXReader()).read(xmlFile);
replInclude(xmlDocument.getRootElement());
return (T) JAXBContext.newInstance(clazz).createUnmarshaller().unmarshal(new StringReader(xmlDocument.asXML()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 这是一个递归
* 目的是替换文档中的include标签
* 还有一个tablepage的问题有待解决
* 该方法有待大量include测试
*
* @param srcEle
* @throws Exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static void replInclude(Element srcEle) throws Exception {
Element inc = srcEle.element("include");
if (inc != null) {
File file = new File(
JaxbUtil.class.getClassLoader().getResource("xml-config/" + inc.attributeValue("file")).getFile());
if (!file.exists()) {
throw new Exception("文件" + inc.attributeValue("file") + "没有找到!!!");
}
// 使用文件中的内容替换include
Element rootEle = (new SAXReader()).read(file).getRootElement();
// TODO:这个被包含页面不能有tablepage标签的问题要解决一下
if (rootEle.getName().equalsIgnoreCase("tablepage")) {
throw new Exception("被包含页面不能有tablepage标签!!!");
}
replInclude(rootEle);
List content = srcEle.content();// inc.getParent()
content.set(content.indexOf(inc), rootEle);
}
List<Element> list = srcEle.elements();
if (!CollectionUtils.isEmpty(list)) {
for (Element element : list) {
replInclude(element);
}
}
}
// /**
// * JavaBean转换成xml 默认编码UTF-8
// *
// * @param obj
// * @param writer
// * @return
// */
// public static String convertToXml(Object obj) {
// return convertToXml(obj, "UTF-8");
// }
// /**
// * JavaBean转换成xml
// *
// * @param obj
// * @param encoding
// * @return
// */
// public static String convertToXml(Object obj, String encoding) {
// String result = null;
// try {
// JAXBContext context = JAXBContext.newInstance(obj.getClass());
// Marshaller marshaller = context.createMarshaller();
// marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
//
// StringWriter writer = new StringWriter();
// marshaller.marshal(obj, writer);
// result = writer.toString();
// } catch (Exception e) {
// e.printStackTrace();
// }
//
// return result;
// }
}
而PageHelper则是数据–>pdf的封装
package com.ufgov.util.pdf.util;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.ufgov.util.pdf.entity.Table;
import com.ufgov.util.pdf.entity.Tablepage;
import com.ufgov.util.pdf.entity.Td;
import com.ufgov.util.pdf.entity.Title;
import com.ufgov.util.pdf.entity.Tr;
/**
* 导出pdf核心帮助类
* xml与数据装配核心方法
*/
public class PdfHelper {
/**
* xml与数据装配页面<br>
* 核心方法,负责输出文档
* @param filePath xml在xml-config下的路径不包括xml-config/及最后的.xml
* @param dataMap 数据源
* @param response
* @throws Exception
*/
public static void convert(String filePath,Map<String, List<Map<String, Object>>> dataMap, HttpServletResponse response)
throws Exception {
try {
Tablepage tablepage = JaxbUtil.converyToJavaBean(filePath, Tablepage.class);
String fileName = tablepage.getFileName();
// BaseFont baseFont = BaseFont.createFont("STSong-Light",
// "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
// Font font = new Font(baseFont, 10, Font.NORMAL);
if (StringUtils.isEmpty(fileName)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmssSSS");
fileName = sdf.format(new Date()) + ".pdf";
System.out.println("文件名为空,将使用默认文件名:" + fileName);
}
// 设置response属性
response.reset();
response.setHeader("content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, "utf-8"));
response.setCharacterEncoding("utf-8");
response.setContentType("application/pdf");
// TODO:这个A4要不要做配置?
Rectangle rectPageSize = new Rectangle(PageSize.A4);
Document document = new Document(rectPageSize, -50, -50, 10, 20);
// 这个地方如果拿到注会用的话,要改成使用response输出
// File file = new File(fileName);
// FileOutputStream out = new FileOutputStream(file);
// PdfWriter.getInstance(document, out);
PdfWriter.getInstance(document, response.getOutputStream());
document.open();
// PdfPTable table_title1 = new PdfPTable(1);
// setTitle(document, title, table_title1);
List<Table> tableList = tablepage.getTableList();
// int size = tableList.size();
// if (tableList == null || size < 1) {
// return;
// }
if (CollectionUtils.isEmpty(tableList)) {
return;
}
for (Table table : tableList) {
String rotate = table.getRotate();
Rectangle rect = new Rectangle(PageSize.A4);
// 添加横向支持
if (rotate.equals("true")) {
document.setPageSize(rect.rotate());
}
document.newPage();
PdfPTable table1 = new PdfPTable(12);
// table1.setLockedWidth(true);
Title t = table.getTitle();
PdfPTable tableTitle = new PdfPTable(1);
if (t != null && t.getText() != null) {
setTitle(document, t, tableTitle);
}
List<Tr> trList = table.getTrList();
String entity = table.getEntity();
List<Map<String, Object>> list = dataMap.get(entity);
if (CollectionUtils.isEmpty(list)) {
System.out.println("警告:出现空值!");
}
if (trList == null || trList.size() < 1) {
document.add(table1);
continue;
}
for (Tr tr : trList) {
// 检测是否是list表格
if (!StringUtils.isEmpty(tr.getForeach()) && tr.getForeach().equalsIgnoreCase("true")) {
String entities = tr.getEntities();
// 没有填充数据,不循环
if (StringUtils.isEmpty(entities) || !dataMap.containsKey(entities)) {
System.out.println("entities设置不正确!");
continue;
}
// 强制转换为List<Map<String,Object>>类型
List<Map<String, Object>> dataList = (List<Map<String, Object>>) dataMap.get(entities);
if (CollectionUtils.isEmpty(list)) {
System.out.println("entities为空哟!");
continue;
}
for (Map<String, Object> en : dataList) {
renderTd(table1, tr, en);
}
} else {
renderTd(table1, tr, list.get(0));
}
}
document.add(table1);
}
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void renderTd(PdfPTable table1, Tr tr, Map<String, Object> obj)
throws DocumentException, IOException {
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font font = new Font(baseFont, 10, Font.NORMAL);
// TODO:这个地方可以做成静态数据,也可以不做,只是为了输出的时候更简单
Map<String, Integer> posMap = new HashMap<String, Integer>();
posMap.put("center", Element.ALIGN_CENTER);
posMap.put("left", Element.ALIGN_LEFT);
posMap.put("right", Element.ALIGN_RIGHT);
posMap.put("top", Element.ALIGN_TOP);
posMap.put("middle", Element.ALIGN_MIDDLE);
posMap.put("bottom", Element.ALIGN_BOTTOM);
List<Td> tdList = tr.getTdList();
if (tdList == null || tdList.size() < 1) {
return;
}
for (Td td : tdList) {
String text = td.getText();
if (text.startsWith("{{") && text.endsWith("}}")) {
String key = text.replace("{{", "").replace("}}", "");
text = obj.containsKey(key) && obj.get(key) != null ? obj.get(key).toString() : "";
}
PdfPCell cell = new PdfPCell(new Paragraph(text, font));
String borderWidth = td.getBorderWidth();
float[] borderArr = getBorder(borderWidth);
if (borderArr != null) {
cell.setBorderWidthTop(borderArr[0]);
cell.setBorderWidthRight(borderArr[1]);
cell.setBorderWidthBottom(borderArr[2]);
cell.setBorderWidthLeft(borderArr[3]);
}
cell.setColspan(Integer.parseInt(td.getColspan()));
cell.setRowspan(Integer.parseInt(td.getRowspan()));
cell.setHorizontalAlignment(posMap.get(td.getAlign()));
cell.setVerticalAlignment(posMap.get(td.getvAlign()));
cell.setMinimumHeight(20f);
// if (arr[2] != 0) {
// cell.setFixedHeight(arr[2]);
// }
table1.addCell(cell);
}
}
/**
* 在这里设置边框。暂时没有什么好的方法
*/
private static float[] getBorder(String borderWidth) {
if (borderWidth == null) {
return null;
}
float[] borderArr = new float[4];
String[] borderWidths = borderWidth.split("\\s+");
// 校验边框
int borderNum = borderWidths.length;
switch (borderNum) {
case 1:
borderArr[0] = Float.parseFloat(borderWidths[0]);
borderArr[1] = borderArr[0];
borderArr[2] = borderArr[0];
borderArr[3] = borderArr[0];
break;
case 2:
borderArr[0] = Float.parseFloat(borderWidths[0]);
borderArr[1] = borderArr[0];
borderArr[2] = Float.parseFloat(borderWidths[1]);
borderArr[3] = borderArr[2];
break;
case 3:
borderArr[0] = Float.parseFloat(borderWidths[0]);
borderArr[1] = Float.parseFloat(borderWidths[1]);
borderArr[2] = Float.parseFloat(borderWidths[2]);
borderArr[3] = 0;
break;
case 4:
borderArr[0] = Float.parseFloat(borderWidths[0]);
borderArr[1] = Float.parseFloat(borderWidths[1]);
borderArr[2] = Float.parseFloat(borderWidths[2]);
borderArr[3] = Float.parseFloat(borderWidths[3]);
break;
default:
borderArr[0] = borderArr[1] = borderArr[2] = borderArr[3] = 1;
break;
}
return borderArr;
}
private static void setTitle(Document document, Title title, PdfPTable tableTitle) throws Exception {
PdfPCell cell = new PdfPCell(new Paragraph(title.getText(), new Font(
BaseFont.createFont(title.getFontFamily(), "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED), 17, Font.NORMAL)));
// TODO:这个地方要设置标题的位置,暂时先不写
cell.setHorizontalAlignment(Element.ALIGN_CENTER); // 设置单元格中文本位置(居中:ALIGN_CENTER;靠左:ALIGN_LEFT;靠右:ALIGN_RIGHT)
cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 文本垂直方向位置(靠上:ALIGN_TOP;居中:ALIGN_MIDDLE;靠下:ALIGN_BOTTOM;)
cell.setBorderWidth(0f); // 设置单元格边框,参数都为float
cell.setPaddingBottom(20f); // 设置单元格文本内边距
tableTitle.addCell(cell);
document.add(tableTitle);
}
}
一个调用的示例
3.java代码中调用方法参考如下
@RequestMapping(value="/exportReport")
public void printBir(String reportNo ,HttpServletResponse response) throws Exception{
//在xml-config下的文件名,如果嵌套有文件夹的话,要写上嵌套路径
String fileName = "NewFile2" ;
CpaCpafBir cpaCpafBir=caCpafBirService.selectByReportNo(reportNo);
Map<String, List<Map<String, Object>>> map = new HashMap<String, List<Map<String, Object>>>();
//toMap的参数为BigDecimal的精度
map.put("cpaCpafBir", cpaCpafBir.toMap(null)) ;
PdfHelper.convert(fileName, map, response);
}
如上。
待改进的地方
待改进的地方还是比较多的
首先是include的处理。定义include主要是为了分割xml文件,否则一个巨长的xml其可读性和维护性势必不好,但是发现在处理的时候对于根节点tablepage的处理有些问题。待处理
再就是xml的编写。虽然采用的是类html的方式,但是还是偏向于复杂。也许可以图形化配置。
还有就是dtd的位置问题。目前的方案似乎还不错。看网上很多说法都是Schema取代dtd,不知道会不会更好以下
暂时没有发现其它问题。