系统中有一个读取PDF表单的数据,然后将数据按字段解析出来,存储到数据库的功能。实现思路,大致是先获取PDF的流,把数据导入到xml中,然后逐行读取xml的数据。具体的实现代码有点挫...
package com.rb.common.pdf;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;
import com.itextpdf.text.DocumentException;
import com.rb.owk.commons.lang.base.orm.BusinessException;
/**
* 解析PDF报表T-Q-1,提取报表数据
*
* @author HO274509
*
*/
public class PdfTQ1 {
private PdfTQ1() {
}
private final static Log log = LogFactory.getLog(PdfTQ1.class);
// 要解析的PDF
public static final String RESOURCE = "/pdf/T-Q-1.pdf";// classpath相对路径
// 要填充PDF的XML数据来源
public static final String XMLDATA = "T-Q-1.xml";
// 填充之前的PDF
public static final String SOURCE = "T-Q-1.pdf";
// 填充之后的PDF
public static final String RESULT = "T-Q-1_fill.pdf";
/**
* 打印pdf数据
*
* @throws TransformerException
* @throws TransformerFactoryConfigurationError
* @throws SAXException
* @throws ParserConfigurationException
* @throws IOException
*/
public static void printPdfData() throws IOException,
ParserConfigurationException, SAXException,
TransformerFactoryConfigurationError, TransformerException {
/**
* 获取xml格式数据
*/
InputStream in = PdfTQ4.class.getResourceAsStream(RESOURCE);
// 另一种获取方法 PdfUtil.getXFAData(RESOURCE, file);
ByteArrayOutputStream os = PdfUtil.getXFAData(in);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
/**
* 使用dom4j解析xml,获取数据
*/
SAXReader xmlReader = new SAXReader();
org.dom4j.Document document = null;
try {
// document = xmlReader.read(xml);
document = xmlReader.read(is);
} catch (org.dom4j.DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
log.error("log4j解析出错");
}
// 根节点
Element root = document.getRootElement();
// 表单根节点
Element element = root.element("T_Q_1");
// 填报人相关信息
log.info("-----填报人信息-----");
log.info(String.format("填报部门:%s", element.element("FILLIN_DEPT")
.getText()));
log.info(String.format("填报人:%s", element.element("FILLIN_PERSON")
.getText()));
log.info(String.format("联系电话:%s", element.element("TELEPHONE")
.getText()));
log.info(String.format("责任人:%s", element.element("RES_PERSON")
.getText()));
log.info("-----重要信息系统的停止服务情况-----");
// 表单TQ1001数据根节点:停止服务性质为非预期停止服务的系统停止服务情况
Iterator<Element> it_TQ1001 = element.elementIterator("TQ1001");
int index = 1;
while (it_TQ1001.hasNext()) {// 打印非预期停止服务的系统停止服务情况
Element TQ1001 = it_TQ1001.next();
log.info("停止服务性质:非预期停止服务");
log.info(String.format("序号:%s", index));
log
.info(String.format("信息系统:%s", TQ1001.element("COL1")
.getText()));
if (StringUtils.isNotEmpty(TQ1001.elementText("COL2"))) {
log.info(String.format("备注:%s", TQ1001.elementText("COL2")));
}
log.info(String.format("停止服务原因:%s", TQ1001.elementText("COL3")));
if (StringUtils.isNotEmpty(TQ1001.elementText("COL4"))) {
log.info(String.format("备注:%s", TQ1001.elementText("COL4")));
}
log.info(String.format("事件等级:%s", TQ1001.elementText("COL5")));
log.info(String.format("起始时间:%s", TQ1001.elementText("COL6")));
log.info(String.format("结束时间:%s", TQ1001.elementText("COL7")));
log.info(String.format("影响范围:%s", TQ1001.elementText("COL8")));
if (StringUtils.isNotEmpty(TQ1001.elementText("COL9"))) {
log.info(String.format("个数:%s", TQ1001.elementText("COL9")));
}
log.info(String.format("描述:%s", TQ1001.elementText("COL10")));
index++;
}
// 表单TQ1003数据根节点:停止服务性质为预期停止服务的系统停止服务情况
Iterator<Element> it_TQ1003 = element.elementIterator("TQ1003");
index = 1;
while (it_TQ1003.hasNext()) {// 打印停止服务的系统停止服务情况
Element TQ1002 = it_TQ1003.next();
log.info("停止服务性质:预期停止服务");
log.info(String.format("序号:%s", index));
log
.info(String.format("信息系统:%s", TQ1002.element("COL1")
.getText()));
if (StringUtils.isNotEmpty(TQ1002.elementText("COL7"))) {
log.info(String.format("备注:%s", TQ1002.elementText("COL7")));
}
log.info(String.format("停止服务原因:%s", TQ1002.elementText("COL2")));
if (StringUtils.isNotEmpty(TQ1002.elementText("COL8"))) {
log.info(String.format("备注:%s", TQ1002.elementText("COL8")));
}
log.info(String.format("起始时间:%s", TQ1002.elementText("COL3")));
log.info(String.format("结束时间:%s", TQ1002.elementText("COL4")));
log.info(String.format("影响范围:%s", TQ1002.elementText("COL10")));
if (StringUtils.isNotEmpty(TQ1002.elementText("COL9"))) {
log.info(String.format("个数:%s", TQ1002.elementText("COL9")));
}
log.info(String.format("描述:%s", TQ1002.elementText("COL6")));
index++;
}
// 表单TQ1006数据根节点:核心业务系统重要性能指标
Element TQ1006 = element.element("TQ1006");
log.info("-----核心系统重要性能指标-----");
log.info(String.format("系统可用率-->数量:%s,备注:%s", TQ1006.element("COL1")
.getText(), TQ1006.element("COL19").getText()));
log.info(String.format("批处理的平均批处理用时-->数量:%s,备注:%s", TQ1006.element(
"COL2").getText(), TQ1006.element("COL20").getText()));
log.info(String.format("CPU平均使用率-->数量:%s,备注:%s", TQ1006.element("COL3")
.getText(), TQ1006.element("COL21").getText()));
log.info(String.format("CPU高峰使用率-->数量:%s,备注:%s", TQ1006.element("COL4")
.getText(), TQ1006.element("COL22").getText()));
log.info(String.format("内存平均使用率-->数量:%s,备注:%s", TQ1006.element("COL5")
.getText(), TQ1006.element("COL23").getText()));
log.info(String.format("磁盘空间占有率峰值-->数量:%s,备注:%s", TQ1006
.element("COL8").getText(), TQ1006.element("COL26").getText()));
log.info(String.format("处理能力---"));
log.info(String.format("日均交易笔数-->数量:%s,备注:%s", TQ1006.element("COL9")
.getText(), TQ1006.element("COL27").getText()));
log.info(String.format("日交易笔数峰值-->数量:%s,备注:%s", TQ1006.element("COL10")
.getText(), TQ1006.element("COL28").getText()));
log.info(String.format("系统可承载的最大交易并发数-->数量:%s,备注:%s", TQ1006.element(
"COL11").getText(), TQ1006.element("COL29").getText()));
log.info(String.format("交易成功率-->数量:%s,备注:%s", TQ1006.element("COL12")
.getText(), TQ1006.element("COL30").getText()));
log.info(String.format("账户数及变动---"));
log.info(String.format("公司账户及增减---"));
log
.info(String.format("公司账户:%s,同比:%s,环比:%s,备注:%s", TQ1006
.element("COL13").getText(), TQ1006.element("COL14")
.getText(), TQ1006.element("COL15").getText(), TQ1006
.element("COL31").getText()));
log.info(String.format("个人账户及增减---"));
log
.info(String.format("个人账户:%s,同比:%s,环比:%s,备注:%s", TQ1006
.element("COL16").getText(), TQ1006.element("COL17")
.getText(), TQ1006.element("COL18").getText(), TQ1006
.element("COL32").getText()));
// 表单TQ1007数据根节点:
Element TQ1007 = element.element("TQ1007");
log.info("-----核心网络系统运行情况-----");
log.info(String.format("业务时段平均带宽占用情况(生产中心或中心机房到一级分支机构):%s,备注:%s",
TQ1007.elementText("COL1"), TQ1007.elementText("COL15")));
log.info(String.format("业务时段平均带宽占用情况(互联网出口):%s,备注:%s", TQ1007
.elementText("COL2"), TQ1007.elementText("COL16")));
log.info("------网上银行系统运行情况-----");
log.info(String.format("日均交易笔数 数量:%s,备注:%s",
TQ1007.elementText("COL3"), TQ1007.elementText("COL17")));
log.info(String.format("日交易笔数峰 数量:%s,备注:%s",
TQ1007.elementText("COL4"), TQ1007.elementText("COL18")));
log.info(String.format("系统可承载的最大交易并发 数量:%s,备注:%s", TQ1007
.elementText("COL5"), TQ1007.elementText("COL19")));
log.info(String.format("平均在线并发用户数 数量:%s,备注:%s", TQ1007
.elementText("COL6"), TQ1007.elementText("COL20")));
log.info(String.format("最大在线并发用户数 数量:%s,备注:%s", TQ1007
.elementText("COL7"), TQ1007.elementText("COL21")));
log.info(String.format("系统可承载的最大在线并发数 数量:%s,备注:%s", TQ1007
.elementText("COL8"), TQ1007.elementText("COL22")));
log.info("-----银行卡系统运行情况-----");
log.info(String.format("日均交易笔数 数量:%s,备注:%s",
TQ1007.elementText("COL9"), TQ1007.elementText("COL23")));
log.info(String.format("日交易笔数峰数 数量:%s,备注:%s", TQ1007
.elementText("COL10"), TQ1007.elementText("COL24")));
log.info(String.format("系统可承载的最大交易并发数 数量:%s,备注:%s", TQ1007
.elementText("COL11"), TQ1007.elementText("COL25")));
log.info("-----第三方存管系统运行情况-----");
log.info(String.format("日均交易笔数 数量:%s,备注:%s", TQ1007
.elementText("COL12"), TQ1007.elementText("COL26")));
log.info(String.format("日交易笔数峰数 数量:%s,备注:%s", TQ1007
.elementText("COL13"), TQ1007.elementText("COL27")));
log.info(String.format("系统可承载的最大交易并发数 数量:%s,备注:%s", TQ1007
.elementText("COL14"), TQ1007.elementText("COL28")));
log.info("-----数据中心(中心机房)外部异常情况-----");
log.info(String.format("数据中心市电中断次数 数量:%s,备注:%s", TQ1007
.elementText("COL29"), TQ1007.elementText("COL30")));
log.info(String.format("数据中心由于外部原因导致网络通讯中断次数 数量:%s,备注:%s", TQ1007
.elementText("COL31"), TQ1007.elementText("COL32")));
}
public static Map<String, Object> getTq1Data() throws IOException,
ParserConfigurationException, SAXException,
TransformerFactoryConfigurationError, TransformerException {
Map<String, Object> tq1Map = new HashMap<String, Object>();
/**
* 获取xml格式数据
*/
InputStream in = PdfTQ4.class.getResourceAsStream(RESOURCE);
ByteArrayOutputStream os = PdfUtil.getXFAData(in);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
/**
* 使用dom4j解析xml,获取数据
*/
SAXReader xmlReader = new SAXReader();
org.dom4j.Document document = null;
try {
// document = xmlReader.read(xml);
document = xmlReader.read(is);
} catch (org.dom4j.DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
log.error("log4j解析出错");
}
// PDF报表在adobe life cycle表单设计里的结构是:pdf报表标识符-》多个表单-》表单内嵌套有数据节点或者子表单
// PDF报表的XML数据结构(基本一致,就这四层):ROOT-》报表标识节点-》各个表单根节点=》表单内部数据节点
// 根节点
Element root = document.getRootElement();
// 报表根节点
Element element = root.element("T_Q_1");
// 填报人相关信息
tq1Map.put("ISEMPTY", element.elementText("isempty"));
tq1Map.put("FILLINDEPT", element.elementText("FILLIN_DEPT"));
tq1Map.put("FILLINPERSON", element.elementText("FILLIN_PERSON"));
tq1Map.put("TELEPHONE", element.elementText("TELEPHONE"));
tq1Map.put("RESPERSON", element.elementText("RES_PERSON"));
List<Map<String, Object>> tq1001List = new ArrayList<Map<String,Object>>();
Iterator<Element> it_TQ1001 = element.elementIterator("TQ1001");
int i=1;
while (it_TQ1001.hasNext()) {
Element tq1001 = it_TQ1001.next();
Iterator<Element> it = tq1001.elementIterator();
Map<String , Object> tq1001Map = new HashMap<String, Object>();
tq1001Map.put("INDEX", i++);
while (it.hasNext()) {
Element tmp = it.next();
log.info(tmp.getName() + ":::::::" + tmp.getTextTrim());
tq1001Map.put(tmp.getName(), tmp.getTextTrim());
}
if(StringUtils.isEmpty((String)tq1001Map.get("COL9"))){
tq1001Map.put("COL9", null);
}
tq1001List.add(tq1001Map);
}
tq1Map.put("tq1001List", tq1001List);
List<Map<String, Object>> tq1003List = new ArrayList<Map<String,Object>>();
Iterator<Element> it_TQ1003 = element.elementIterator("TQ1003");
i=1;
while (it_TQ1003.hasNext()) {
Element tq1003 = it_TQ1003.next();
Iterator<Element> it = tq1003.elementIterator();
Map<String , Object> tq1003Map = new HashMap<String, Object>();
tq1003Map.put("INDEX", i++);
while (it.hasNext()) {
Element tmp = it.next();
log.info(tmp.getName() + ":::::::" + tmp.getTextTrim());
tq1003Map.put(tmp.getName(), tmp.getTextTrim());
}
if(StringUtils.isEmpty((String)tq1003Map.get("COL9"))){
tq1003Map.put("COL9", null);
}
tq1003List.add(tq1003Map);
}
tq1Map.put("tq1003List", tq1003List);
Element TQ1006 = element.element("TQ1006");
Iterator<Element> it_TQ1006 = TQ1006.elementIterator();
while (it_TQ1006.hasNext()) {
Element tmp = it_TQ1006.next();
log.info(tmp.getName() + ":::::::" + tmp.getTextTrim());
tq1Map.put(tmp.getName(), tmp.getTextTrim());
}
Element TQ1007 = element.element("TQ1007");
Iterator<Element> it_TQ1007 = TQ1007.elementIterator();
while (it_TQ1007.hasNext()) {
Element tmp = it_TQ1007.next();
log.info(tmp.getName()+"TQ1007" + ":::::::" + tmp.getTextTrim());
tq1Map.put(tmp.getName()+"TQ1007", tmp.getTextTrim());
}
return tq1Map;
}
/**
* 填充成新的pdf之后,会提示pdf文档自创建后被修改,无法再使用扩展功能,这个不知道为什么
*
* @throws IOException
* @throws DocumentException
*/
public static void fillPdf() throws IOException, DocumentException {
PdfUtil.manipulatePdf(RESOURCE, XMLDATA, RESULT);
}
public static void main(String[] args) throws IOException,
ParserConfigurationException, SAXException,
TransformerFactoryConfigurationError, TransformerException,
DocumentException, BusinessException {
// 提取pdf数据,打印
printPdfData();
// // 以XML形式导出pdf数据
File file = new File(XMLDATA);
PdfUtil.getXFAData(RESOURCE, file);
// 根据模板和数据生成PDF
fillPdf();
}
}
调用的pdfutil代码如下
package com.rb.common.pdf;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.XfaForm;
/**
* pdf工具类
* 依赖ITEXT
* @author HO274509
*
*/
public class PdfUtil {
/**
* Reads the data from a PDF containing an XFA form.
* 解析基于XML Forms Architecture的pdf,导出xml形式的表单数据写入到文件中
* @param src
* the original PDF
* @param dest
* the data in XML format
* @throws IOException
* @throws ParserConfigurationException
* @throws SAXException
* @throws TransformerFactoryConfigurationError
* @throws TransformerException
*/
public static void getXFAData(String src, File file) throws IOException,
ParserConfigurationException, SAXException,
TransformerFactoryConfigurationError, TransformerException {
FileOutputStream os = new FileOutputStream(file);
PdfReader reader = new PdfReader(src);
XfaForm xfa = new XfaForm(reader);
Node node = xfa.getDatasetsNode();
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if ("data".equals(list.item(i).getLocalName())) {
node = list.item(i);
break;
}
}
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.transform(new DOMSource(node), new StreamResult(os));
reader.close();
}
/**
* Reads the data from a PDF containing an XFA form.
* 解析基于XML Forms Architecture的pdf,导出xml形式的表单数据写入到文件中
* @param src
* the original PDF
* @throws IOException
* @throws ParserConfigurationException
* @throws SAXException
* @throws TransformerFactoryConfigurationError
* @throws TransformerException
*/
public static ByteArrayOutputStream getXFAData(InputStream is) throws IOException,
ParserConfigurationException, SAXException,
TransformerFactoryConfigurationError, TransformerException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(is);
XfaForm xfa = new XfaForm(reader);
Node node = xfa.getDatasetsNode();
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if ("data".equals(list.item(i).getLocalName())) {
node = list.item(i);
break;
}
}
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.transform(new DOMSource(node), new StreamResult(os));
reader.close();
return os;
}
/**
* Reads the data from a PDF containing an XFA form.
* 解析基于XML Forms Architecture的pdf,导出xml形式的表单数据写入到文件中
* @param src
* the original PDF
* @param dest
* the data in XML format
* @throws IOException
* @throws ParserConfigurationException
* @throws SAXException
* @throws TransformerFactoryConfigurationError
* @throws TransformerException
*/
public static void getXFAData(InputStream is, File file) throws IOException,
ParserConfigurationException, SAXException,
TransformerFactoryConfigurationError, TransformerException {
FileOutputStream os = new FileOutputStream(file);
PdfReader reader = new PdfReader(is);
XfaForm xfa = new XfaForm(reader);
Node node = xfa.getDatasetsNode();
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if ("data".equals(list.item(i).getLocalName())) {
node = list.item(i);
break;
}
}
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.transform(new DOMSource(node), new StreamResult(os));
reader.close();
}
/**
* Checks if a PDF containing an interactive form uses AcroForm technology,
* XFA technology, or both. Also lists the field names.
*
* 不支持adobe designer设计的xfa格式的pdf
* @param src
* the original PDF
* @param dest
* a text file containing form info.
* @throws IOException
*/
public void getFieldnames(String src, String dest) throws IOException {
PrintStream out = new PrintStream(new FileOutputStream(dest));
PdfReader reader = new PdfReader(src);
AcroFields form = reader.getAcroFields();
XfaForm xfa = form.getXfa();
out.println(xfa.isXfaPresent() ? "XFA form" : "AcroForm");
Set<String> fields = form.getFields().keySet();
for (String key : fields) {
out.println(key);
}
out.flush();
out.close();
}
/**
* Reads the XML that makes up an XFA form.
*
* @param src
* the original PDF file
* @param dest
* the resulting XML file
* @throws IOException
* @throws ParserConfigurationException
* @throws SAXException
* @throws TransformerFactoryConfigurationError
* @throws TransformerException
*/
public void readXfa(String src, String dest) throws IOException,
ParserConfigurationException, SAXException,
TransformerFactoryConfigurationError, TransformerException {
FileOutputStream os = new FileOutputStream(dest);
PdfReader reader = new PdfReader(src);
XfaForm xfa = new XfaForm(reader);
Document doc = xfa.getDomDocument();
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.transform(new DOMSource(doc), new StreamResult(os));
reader.close();
}
/**
* Manipulates a PDF file src with the file dest as result
* @param src the original PDF
* @param xml the XML data that needs to be added to the XFA form
* @param dest the resulting PDF
* @throws IOException
* @throws DocumentException
*/
public static void manipulatePdf(String src, String xml, String dest)
throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader,
new FileOutputStream(dest), '\0', true);
AcroFields form = stamper.getAcroFields();
XfaForm xfa = form.getXfa();
xfa.fillXfaForm(new FileInputStream(xml));
stamper.close();
}
}