文章目录

  • 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,不知道会不会更好以下

暂时没有发现其它问题。