Java使用POI根据模板导出Word

最近从新写了一下根据Word模板导出Word。

注意:
Word只包含表格和段落,不使用表格布局。
图片样式也保留,但是预先需要知道图片的资源ID。
删除多余模块时,有顶部对不齐的问题。
可能还存在其他细节问题。

首先模板样式:

Word模板

下面是导出来的Word:

Word模板

下面贴上代码:

package com.acgist.word;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
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 java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRelation;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;

public class WordUtils {

	private WordData wordData = null;
	private XWPFDocument document = null;
	
	private static final String TEXT_PATH = "declare namespace w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"; $this//w:t";
	
	private Map<String, XWPFParagraph> xwpfParagraphs = new HashMap<String, XWPFParagraph>(); // 多项
	
	private static final String[] BASE_TAGS = {"${age}", "${job}", "${name}", "${email}", "${mobile}", "${address}"};
	
	private static final String NEW_LINE = "\\\\r\\\\n|\\\\n|\\\\r|\\r\\n|\\n|\\r";
	
	private static final String TAG_REGEX = "\\$\\{[a-z_]+\\}"; // 标签正则表达式

    public static void main(String[] args) {
        WordUtils utils = new WordUtils();
        utils.build("e:/company/word/JM0001.docx");
//      utils.build("e:/company/word/JM0001YA.docx");
        WordData wordData = new WordData();
        wordData.setAge("23岁");
        wordData.setJob("工作");
        wordData.setName("喻胜");
        wordData.setAddress("广东广州");
        wordData.setMobile("13888888888");
        wordData.setEmail("8888@qq.com");
        wordData.setHead("e:/company/word/sign.png");
        Map<String, String> item = new TreeMap<String, String>();
        item.put("技能证书", "北大、擎华");
        item.put("兴趣爱好", "动漫、游戏动漫、游戏动漫、游戏动漫、游戏动漫\r\n、游戏动漫、游戏动漫、游戏动漫、游戏动漫、游戏动漫、游戏动漫、游戏动漫、游戏动漫、游戏动漫、游戏");
        Map<String, List<WordDataItem>> items = new TreeMap<String, List<WordDataItem>>();
        List<WordDataItem> lista = new ArrayList<WordDataItem>();
        lista.add(new WordDataItem("2012-2012", "五百丁", "程序员", "zheshishiyigeceshi"));
        lista.add(new WordDataItem("2013-2013", "百度", "程序员", "zheshishiyigeceshi"));
        items.put("教育背景", lista);
        wordData.setHeadId("rId8");
        wordData.sorts.add("教育背景");
        wordData.sorts.add("技能证书");
        wordData.sorts.add("兴趣爱好");
//      List<WordDataItem> listb = new ArrayList<WordDataItem>();
//      listb.add(new WordDataItem("123", "北大", "北大"));
//      listb.add(new WordDataItem("123", "北大", "北大"));
//      items.put("工作经历", listb);
        wordData.setItems(items);
        wordData.setItem(item);
        utils.write("e:/company/word/t.docx", wordData);
    }
	
	/**
	 * 多项信息分栏
	 */
	public enum Items_col_type {
		all_in_one, // 全部在一行
		time_post_in_one,
		unit_post_in_one,
		time_unit_in_one,
		all_aplit // 全部分开
	}
	
	private Items_col_type items_col_type;
	
	private static final String TAG_AGE = "${age}";
	private static final String TAG_JOB = "${job}";
	private static final String TAG_NAME = "${name}";
	private static final String TAG_EMAIL = "${email}";
	private static final String TAG_MOBILE = "${mobile}";
	private static final String TAG_ADDRESS = "${address}";
	
	private static final String TAG_ITEM_TITLE = "${item_title}";
	private static final String TAG_ITEM_CONTENT = "${item_content}";
	
	private static final String TAG_ITEMS_TIME = "${items_time}";
	private static final String TAG_ITEMS_UNIT = "${items_unit}";
	private static final String TAG_ITEMS_POST = "${items_post}";
	private static final String TAG_ITEMS_TITLE = "${items_title}";
	private static final String TAG_ITEMS_CONTENT = "${items_content}";
	
	/**
	 * 处理2007+的WORD
	 * @param filePath 文件地址
	 * @return word内容
	 * @throws IOException 
	 */
	public void build(String filePath) {
		InputStream inputStream = null;
		try {
			inputStream = new FileInputStream(new File(filePath));
			document = new XWPFDocument(inputStream);
			XWPFParagraph xwpfParagraph = null;
			Iterator<XWPFParagraph> iterator = document.getParagraphs().iterator();
			while(iterator.hasNext()) {
				xwpfParagraph = iterator.next();
				if(xwpfParagraph != null) {
					String tagName = "";
					String tagNameTemp = null;
					for(XWPFRun xwpfRun : xwpfParagraph.getRuns()) {
						tagNameTemp = xwpfRun.getCTR().newCursor().getTextValue();
						if(StringUtils.isNotEmpty(tagNameTemp)) {
							tagName += tagNameTemp;
							setXwpfParagraphs(tagNameTemp, xwpfParagraph);
						}
					}
					if(StringUtils.isNotEmpty(tagName))
						setXwpfParagraphs(tagName.trim(), xwpfParagraph);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(inputStream);
		}
		if(xwpfParagraphs.get(TAG_ITEMS_TIME) == xwpfParagraphs.get(TAG_ITEMS_UNIT) && xwpfParagraphs.get(TAG_ITEMS_UNIT) == xwpfParagraphs.get(TAG_ITEMS_POST))
			items_col_type = Items_col_type.all_in_one;
		else if(xwpfParagraphs.get(TAG_ITEMS_TIME) == xwpfParagraphs.get(TAG_ITEMS_UNIT))
			items_col_type = Items_col_type.time_unit_in_one;
		else if(xwpfParagraphs.get(TAG_ITEMS_TIME) == xwpfParagraphs.get(TAG_ITEMS_POST))
			items_col_type = Items_col_type.time_post_in_one;
		else if(xwpfParagraphs.get(TAG_ITEMS_UNIT) == xwpfParagraphs.get(TAG_ITEMS_POST))
			items_col_type = Items_col_type.unit_post_in_one;
		else
			items_col_type = Items_col_type.all_aplit;
	}
	

	/**
	 * 设置 标签和内容的对应
	 * @param key 标签
	 * @param xwpfParagraph 对应的段落
	 */
	private void setXwpfParagraphs(String key, XWPFParagraph xwpfParagraph) {
		if(key.matches(TAG_REGEX))
			this.xwpfParagraphs.put(key, xwpfParagraph);
	}

	/**
	 * 写出
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 */
	public void write(String path, WordData wordData) {
		if(StringUtils.isEmpty(path))
			return;
		if(wordData == null)
			return;
		this.wordData = wordData;
		
		this.modifyBase();
		this.modifyHead();
		this.modifySort();
		this.modifyItem();
		this.modifyItems();
		
		remove(TAG_ITEM_TITLE);
		remove(TAG_ITEM_CONTENT);
		switch (this.items_col_type) {
			case all_in_one:
				remove(TAG_ITEMS_TIME);
			break;
			case time_post_in_one:
			case time_unit_in_one:
				remove(TAG_ITEMS_UNIT);
				remove(TAG_ITEMS_POST);
			break;
			case unit_post_in_one:
				remove(TAG_ITEMS_TIME);
				remove(TAG_ITEMS_UNIT);
			break;
			case all_aplit:
				remove(TAG_ITEMS_TIME);
				remove(TAG_ITEMS_POST);
				remove(TAG_ITEMS_UNIT);
			break;
		}
		remove(TAG_ITEMS_TITLE);
		remove(TAG_ITEMS_CONTENT);
		
		Iterator<XWPFParagraph> iterator = document.getParagraphs().iterator();
		while(iterator.hasNext()) {
			XWPFParagraph xwpfParagraph = iterator.next();
			if(xwpfParagraph.getCTP().isNil())
				continue;
			XmlCursor xmlCursor = xwpfParagraph.getCTP().newCursor();
			if(xwpfParagraph.getCTP().getPPr().getSectPr() != null)
				xmlCursor.removeXml();
		}
		
		try {
			document.write(new FileOutputStream(path));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 删除段落
	 */
	private void remove(String tag) {
		if(StringUtils.isEmpty(tag))
			return;
		List<XWPFParagraph> xwpfParagraphs = document.getParagraphs();
		for (int index = 0; index < xwpfParagraphs.size(); index++) {
			if(xwpfParagraphs.get(index) == this.xwpfParagraphs.get(tag)) {
				document.removeBodyElement(index);
				break;
			}
		}
	}
	
	/**
	 * 设置排序的
	 */
	private void modifySort() {
		List<String> sorts = wordData.sorts;
		for (String sort : sorts) {
			if(wordData.getItem().keySet().contains(sort)) {
				createItem(sort, wordData.getItem().get(sort));
				wordData.getItem().remove(sort);
			} else if (wordData.getItems().keySet().contains(sort)) {
				createItems(sort, wordData.getItems().get(sort));
				wordData.getItems().remove(sort);
			}
		}
	}
	
	/**
	 * 基本信息
	 */
	private void modifyBase() {
		for (String key : BASE_TAGS) {
			XWPFParagraph xwpfParagraph = xwpfParagraphs.get(key);
			String content = null;
			if(TAG_AGE.equals(key))
				content = this.wordData.getAge();
			else if(TAG_JOB.equals(key))
				content = this.wordData.getJob();
			else if(TAG_NAME.equals(key))
				content = this.wordData.getName();
			else if(TAG_EMAIL.equals(key))
				content = this.wordData.getEmail();
			else if(TAG_MOBILE.equals(key))
				content = this.wordData.getMobile();
			else if(TAG_ADDRESS.equals(key))
				content = this.wordData.getAddress();
			setText(key, content, xwpfParagraph);
		}
	}
	
	/**
	 * 单项信息
	 */
	private void modifyItem() {
		Map<String, String> itemMap = wordData.getItem();
		Iterator<Entry<String, String>> iterator = itemMap.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry<String, String> entry = iterator.next();
			String key = entry.getKey();
			String value = entry.getValue();
			
			createItem(key, value);
		}
	}
	
	/**
	 * 创建单项
	 */
	private void createItem(String key, String value) {
		if(key == null || value == null)
			return;
		XWPFParagraph title = xwpfParagraphs.get(TAG_ITEM_TITLE);
		XWPFParagraph newTitle = new XWPFParagraph((CTP) title.getCTP().copy(), title.getBody());
		setText(TAG_ITEM_TITLE, key, newTitle);
		document.createParagraph();
		document.setParagraph(newTitle, document.getParagraphs().size() - 1);
		
		XWPFParagraph content = xwpfParagraphs.get(TAG_ITEM_CONTENT);
		XWPFParagraph newContent = new XWPFParagraph((CTP)content.getCTP().copy(), content.getBody());
		setText(TAG_ITEM_CONTENT, value, newContent);
		document.createParagraph();
		document.setParagraph(newContent, document.getParagraphs().size() - 1);
	}

	/**
	 * 多项信息
	 */
	private void modifyItems() {
		Map<String, List<WordDataItem>> itemMap = wordData.getItems();
		Iterator<Entry<String, List<WordDataItem>>> iterator = itemMap.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry<String, List<WordDataItem>> entry = iterator.next();
			String key = entry.getKey();
			List<WordDataItem> values = entry.getValue();
			
			this.createItems(key, values);
		}
	}

	private void createItems(String key, List<WordDataItem> values) {
		XWPFParagraph title = xwpfParagraphs.get(TAG_ITEMS_TITLE);
		XWPFParagraph newTitle = new XWPFParagraph((CTP) title.getCTP().copy(), title.getBody());
		setText(TAG_ITEMS_TITLE, key, newTitle);
		document.createParagraph();
		document.setParagraph(newTitle, document.getParagraphs().size() - 1);
		for(WordDataItem wordDataItem : values) {
			switch (this.items_col_type) {
				case all_in_one: {
					XWPFParagraph oldParagraph = xwpfParagraphs.get(TAG_ITEMS_TIME);
					XWPFParagraph newParagraph = new XWPFParagraph((CTP) oldParagraph.getCTP().copy(), oldParagraph.getBody());
					setText(TAG_ITEMS_TIME, wordDataItem.getTime(), newParagraph);
					setText(TAG_ITEMS_UNIT, wordDataItem.getUnit(), newParagraph);
					setText(TAG_ITEMS_POST, wordDataItem.getPost(), newParagraph);
					document.createParagraph();
					document.setParagraph(newParagraph, document.getParagraphs().size() - 1);
				}
				break;
				case time_post_in_one: {
					XWPFParagraph oldUnitParagraph = xwpfParagraphs.get(TAG_ITEMS_UNIT);
					XWPFParagraph newUnitParagraph = new XWPFParagraph((CTP) oldUnitParagraph.getCTP().copy(), oldUnitParagraph.getBody());
					setText(TAG_ITEMS_UNIT, wordDataItem.getUnit(), newUnitParagraph);
					document.createParagraph();
					document.setParagraph(newUnitParagraph, document.getParagraphs().size() - 1);
					
					XWPFParagraph oldPostParagraph = xwpfParagraphs.get(TAG_ITEMS_POST);
					XWPFParagraph newPostParagraph = new XWPFParagraph((CTP) oldPostParagraph.getCTP().copy(), oldPostParagraph.getBody());
					setText(TAG_ITEMS_POST, wordDataItem.getPost(), newPostParagraph);
					setText(TAG_ITEMS_TIME, wordDataItem.getTime(), newPostParagraph);
					document.createParagraph();
					document.setParagraph(newPostParagraph, document.getParagraphs().size() - 1);
				}
				break;
				case time_unit_in_one: {
					XWPFParagraph oldPostParagraph = xwpfParagraphs.get(TAG_ITEMS_POST);
					XWPFParagraph newPostParagraph = new XWPFParagraph((CTP) oldPostParagraph.getCTP().copy(), oldPostParagraph.getBody());
					setText(TAG_ITEMS_POST, wordDataItem.getPost(), newPostParagraph);
					document.createParagraph();
					document.setParagraph(newPostParagraph, document.getParagraphs().size() - 1);
					
					XWPFParagraph oldUnitParagraph = xwpfParagraphs.get(TAG_ITEMS_UNIT);
					XWPFParagraph newUnitParagraph = new XWPFParagraph((CTP) oldUnitParagraph.getCTP().copy(), oldUnitParagraph.getBody());
					setText(TAG_ITEMS_UNIT, wordDataItem.getUnit(), newUnitParagraph);
					setText(TAG_ITEMS_TIME, wordDataItem.getTime(), newUnitParagraph);
					document.createParagraph();
					document.setParagraph(newUnitParagraph, document.getParagraphs().size() - 1);
				}
				break;
				case unit_post_in_one: {
					XWPFParagraph oldTimeParagraph = xwpfParagraphs.get(TAG_ITEMS_TIME);
					XWPFParagraph newTimeParagraph = new XWPFParagraph((CTP) oldTimeParagraph.getCTP().copy(), oldTimeParagraph.getBody());
					setText(TAG_ITEMS_TIME, wordDataItem.getTime(), newTimeParagraph);
					document.createParagraph();
					document.setParagraph(newTimeParagraph, document.getParagraphs().size() - 1);
					
					XWPFParagraph oldUnitParagraph = xwpfParagraphs.get(TAG_ITEMS_UNIT);
					XWPFParagraph newUnitParagraph = new XWPFParagraph((CTP) oldUnitParagraph.getCTP().copy(), oldUnitParagraph.getBody());
					setText(TAG_ITEMS_UNIT, wordDataItem.getUnit(), newUnitParagraph);
					setText(TAG_ITEMS_POST, wordDataItem.getPost(), newUnitParagraph);
					document.createParagraph();
					document.setParagraph(newUnitParagraph, document.getParagraphs().size() - 1);
				}
				break;
				case all_aplit: {
					XWPFParagraph oldTimeParagraph = xwpfParagraphs.get(TAG_ITEMS_TIME);
					XWPFParagraph newTimeParagraph = new XWPFParagraph((CTP) oldTimeParagraph.getCTP().copy(), oldTimeParagraph.getBody());
					setText(TAG_ITEMS_TIME, wordDataItem.getTime(), newTimeParagraph);
					document.createParagraph();
					document.setParagraph(newTimeParagraph, document.getParagraphs().size() - 1);
					
					XWPFParagraph oldUnitParagraph = xwpfParagraphs.get(TAG_ITEMS_UNIT);
					XWPFParagraph newUnitParagraph = new XWPFParagraph((CTP) oldUnitParagraph.getCTP().copy(), oldUnitParagraph.getBody());
					setText(TAG_ITEMS_UNIT, wordDataItem.getUnit(), newUnitParagraph);
					document.createParagraph();
					document.setParagraph(newUnitParagraph, document.getParagraphs().size() - 1);
					
					XWPFParagraph oldPostParagraph = xwpfParagraphs.get(TAG_ITEMS_POST);
					XWPFParagraph newPostParagraph = new XWPFParagraph((CTP) oldPostParagraph.getCTP().copy(), oldPostParagraph.getBody());
					setText(TAG_ITEMS_POST, wordDataItem.getUnit(), newPostParagraph);
					document.createParagraph();
					document.setParagraph(newPostParagraph, document.getParagraphs().size() - 1);
				}
				break;
			}
			XWPFParagraph content = xwpfParagraphs.get(TAG_ITEMS_CONTENT);
			XWPFParagraph newContent = new XWPFParagraph((CTP) content.getCTP().copy(), content.getBody());
			setText(TAG_ITEMS_CONTENT, wordDataItem.getContent(), newContent);
			document.createParagraph();
			document.setParagraph(newContent, document.getParagraphs().size() - 1);
		}
	}
	
	/**
	 * 修改头像
	 */
	private void modifyHead() {
		if(StringUtils.isEmpty(wordData.getHeadId()))
			return;
		String head = wordData.getHead();
		String OLD_RID = wordData.getHeadId();
		if(StringUtils.isEmpty(head))
			return;
		File file = new File(head);
		if(!file.isFile() || !file.exists())
			return;
		byte[] imagebis = null;
		InputStream inputStream = null;
		try {
			inputStream = new FileInputStream(file);
			imagebis = new byte[inputStream.available()];
			inputStream.read(imagebis);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(inputStream);
		}
		String rid;
		try {
			rid = document.addPictureData(imagebis, XWPFDocument.PICTURE_TYPE_JPEG);
			document.getPackagePart().removeRelationship(OLD_RID);
			document.getPackagePart().addRelationship(
				document.getRelationById(rid).getPackageRelationship().getTargetURI(),
				TargetMode.INTERNAL,
				XWPFRelation.IMAGES.getRelation(),
				OLD_RID
			);
		} catch (InvalidFormatException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 设置内容
	 * @param key 标签
	 * @param content 内容
	 * @param xwpfParagraph 段落
	 */
	private void setText(String key, String content, XWPFParagraph xwpfParagraph) {
		if(content == null)
			content = "";
		if (xwpfParagraph == null)
			return;
		boolean update = false;
		List<CTR> ctrs = xwpfParagraph.getCTP().getRList(); // 文本框内容
		for (CTR ctr : ctrs) {
			List<CTPicture> ctPictures = ctr.getPictList();
			if(CollectionUtils.isNotEmpty(ctPictures)) {
				CTPicture ctPicture = ctPictures.get(0);
				XmlCursor cursor = ctPicture.newCursor();
				if(cursor.getTextValue().equals(key)) {
					update = true;
					cursor.selectPath(TEXT_PATH);
					if(cursor.toNextSelection())
						cursor.setTextValue(content);
					while(cursor.toNextSelection())
						cursor.removeXml();
				}
			}
		}
		if(update)
			return;
		if(key.equals(xwpfParagraph.getText())) {
			List<CTR> ctrs_ = xwpfParagraph.getCTP().getRList();
			boolean update_ = false;
			for (CTR ctr : ctrs_) {
				XmlCursor cursor = ctr.newCursor();
				cursor.selectPath(TEXT_PATH);
				if(cursor.toNextSelection() && StringUtils.isNotEmpty(cursor.getTextValue()) && !update_) {
					String[] contents = content.split(NEW_LINE);
					update_ = true;
					if(contents.length == 1)
						cursor.setTextValue(content);
					else {
						for (int i = 0; i < contents.length; i++) { // 多行问题
							if(i == 0)
								cursor.setTextValue(contents[0]);
							else {
								ctr.addNewBr();
								ctr.addNewT().setStringValue(contents[i]);
							}
						}
					}
				} else if(StringUtils.isNotEmpty(cursor.getTextValue())) {
					cursor.removeXml();
				}
				while(cursor.toNextSelection())
					cursor.removeXml();
			}
		}
	}
	
}

本代码存在细节问题:如果时间三个信息不在一栏需要另行判断和删除。

代码下载:http://pan.baidu.com/s/1kTzgXcr