Java 对Word文件的生成(基于Apache POI)

本文介绍如何使用 Apache POI 和 poi-tl 模板引擎简化 Java 对 Word 文档的操作,包括文本、图片、表格和列表的渲染,并提供示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 对Word文件的生成(基于Apache POI)

Apache POI 是一个开源的跨平台的对Microsoft Office格式档案具有读和写功能工具。 在Github上有一个开源的Word模版引擎poi-tl ,这个模版引擎是基于Apache POI。主要是为了解决下面的问题:

  • java操作word使用apache poi的复杂性
  • 使用freemarker,转化为xml操作word的难度
  • 依赖服务器上安装软件openoffice来调用转化
  • 依赖windows的word lib库,不具有跨平台性

注意! HSSF - 提供读写Microsoft Excel XLS格式档案的功能。(*.doc),HWPFDocument类 XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。(*.docx),XWPFDocument类 因为这个模版引擎是只使用XWPFDocument类,所以只对*.docx文档生效。

poi-tl demo

调用方法

/*
* datas 是你要渲染的数据
* datas 可以是JavaBean,也可以是Map<String, Object>
*/
XWPFTemplate template = XWPFTemplate.compile("~/file.docx").render(datas);

除了传入模版文件路径,还可以传入模版文件输入流

public static XWPFTemplate compile(InputStream inputStream) {
    .....
    }
datas TO Map<String, Object>

看看数据类转Map的实现

private static Map<String, Object> convert2Map(Object dataSrouce) {
		Map<String, Object> ret = new HashMap<String, Object>();
		try {
			Class<?> clazz = dataSrouce.getClass();
			while (clazz != Object.class) {
				Field[] fields = clazz.getDeclaredFields();
				PropertyDescriptor pd = null;
				for (Field f : fields) {
					pd = new PropertyDescriptor(f.getName(), dataSrouce.getClass());
					Name annotation = f.getAnnotation(Name.class);
					Object value = pd.getReadMethod().invoke(dataSrouce);
					ret.put(null == annotation ? f.getName() : annotation.value(), value);
				}
				clazz = clazz.getSuperclass();
			}
		} catch (Exception e) {
			logger.error("Convert datasource failed.", e);
			throw new RenderException("Convert datasource failed.");
		}
		return ret;
	}
	

利用反射,把datas类和它的父类的字段属性转成Map<String, Object>,Object除外。所以我们传的参数是JavaBean或Map<String, Object>就可以了(传Map参数,会调用同名的的重载方法)。

语法

普通文本

渲染数据为String或者TextRenderData

模版文件中使用:{{template}}

...
Map<String, Object> datas = new HashMap<String, Object>();
datas.put("template", "我是渲染的数据");
// 参数1:颜色 9d55b8;参数2:文本内容
datas.put("title", new TextRenderData("9d55b8", "Deeply in love with the things you love,\n just deepoove."));

...			
  • 文本中可用\n 来进行换行

图片

渲染数据为:PictureRenderData

模版文件中使用:{{@picture}}

/*
* 参数1:宽度;参数2:高度;参数3:图片路径
*/
datas.put("picture", new PictureRenderData(100, 120, "src/test/resources/logo.png"));

表格

渲染数据为:TableRenderData

模版文件中使用:{{#table}}

/**
	 * @param headers 表格头
	 * @param datas 表格数据
	 * @param noDatadesc 没有数据显示的文案
	 * @param width 宽度
	 */
	public TableRenderData(List<RenderData> headers, List<Object> datas,
			String noDatadesc, int width) {
		this.headers = headers;
		this.datas = datas;
		this.noDatadesc = noDatadesc;
		this.width = width;
	}
// 有表格头 有数据
datas.put("table", new TableRenderData(new ArrayList<RenderData>() {
        {
          add(new TextRenderData("1E915D", "province"));
          add(new TextRenderData("1E915D", "city"));
        }
      }, new ArrayList<Object>() {
        {
          add("beijing;beijing");
          add("zhejiang;hangzhou");
        }
      }, "no datas", 0));
    }

如果没有数据,表格会显示“no datas” 更加详细的请参考:poi-tl处理Word表格(Table)的最佳实践

列表

渲染数据为:NumbericRenderData

模版文件中使用:{{*numbering}}

    /**
     * @param numFmt 编号字符
     * @param fmtStyle 编号样式
     * @param numbers 列表内容
     */
    public NumbericRenderData(Pair<Enum, String> numFmt, Style fmtStyle, List<TextRenderData> numbers) {
        this.numFmt = numFmt;
        this.numbers = numbers;
        this.fmtStyle = fmtStyle;
    }
datas.put("unorderlist", new NumbericRenderData(new ArrayList<TextRenderData>(){{
				add(new TextRenderData("Deeply in love with the things you love, just deepoove."));
				add(new TextRenderData("Deeply in love with the things you love, just deepoove."));
				add(new TextRenderData("Deeply in love with the things you love, just deepoove."));
			}}));
datas.put("orderlist", new NumbericRenderData(NumbericRenderData.FMT_DECIMAL, new ArrayList<TextRenderData>(){{
				add(new TextRenderData("Deeply in love with the things you love, just deepoove."));
				add(new TextRenderData("Deeply in love with the things you love, just deepoove."));
				add(new TextRenderData("Deeply in love with the things you love, just deepoove."));
			}}));

NumbericRenderData类中有编号的符号常量

    /**
     * 1. 2. 3.
     */
    public static final Pair<Enum, String> FMT_DECIMAL = Pair.of(STNumberFormat.DECIMAL, "%1.");
    /**
     * 1) 2) 3)
     */
    public static final Pair<Enum, String> FMT_DECIMAL_PARENTHESES = Pair.of(STNumberFormat.DECIMAL,
            "%1)");
    /**
     * ● ● ●
     */
    public static final Pair<Enum, String> FMT_BULLET = Pair.of(STNumberFormat.BULLET, "●");
    /**
     * a. b. c.
     */
    public static final Pair<Enum, String> FMT_LOWER_LETTER = Pair.of(STNumberFormat.LOWER_LETTER,
            "%1.");
    /**
     * i ⅱ ⅲ
     */
    public static final Pair<Enum, String> FMT_LOWER_ROMAN = Pair.of(STNumberFormat.LOWER_ROMAN,
            "%1.");
    /**
     * A. B. C.
     */
    public static final Pair<Enum, String> FMT_UPPER_LETTER = Pair.of(STNumberFormat.UPPER_LETTER,
            "%1.");
    /**
     * Ⅰ Ⅱ Ⅲ
     */
    public static final Pair<Enum, String> FMT_UPPER_ROMAN = Pair.of(STNumberFormat.UPPER_ROMAN,
            "%1.");

样式

Style类

主要样式如下:

  • 颜色
  • 字体
  • 字号
  • 粗体
  • 斜体
  • 删除线
public class Style {
   //颜色
	private String color;
	//字体
	private String fontFamily;
	//字号
	private int fontSize;
	//粗体
	private Boolean isBold;
	//斜体
	private Boolean isItalic;
	//删除线
	private Boolean isStrike;

	public Style() {
	}

	public Style(String color) {
		this.color = color;
	}

	public Style(String fontFamily, int fontSize) {
		this.fontFamily = fontFamily;
		this.fontSize = fontSize;
	}
	
	......
}

poi-tl 的 Change log

v1.2.0 2017-10-12

  1. 新增api:XWPFTemplate compile(InputStream inputStream)
  2. 不兼容升级:文本模板换行符由原先的\\n替换成更符合语言的\n

v1.1.0 2017-09-15

  1. 修复老版本office打开表格模板时出错
  2. 新增列表字符样式:设置编号颜色、字体、粗体、斜体等

v1.0.0

  1. 以插件的思想进行了重新设计
  2. 高度扩展性:语法即插件,像新增插件一样新增语法
  3. 新增工具类BytePictureUtils,便于操作图片的byte[]数据
  4. 新增Annotation @Name
  5. NiceXWPFDocument新增插入段落insertNewParagraph方法
  6. 新增代码生成工具类CodeGenUtils

V0.0.5

  1. bugfix: 解决0.0.4版本解析模板时CTSignedTwips类加载不到的问题
  2. new feature: 新增列表语法*,支持对有序列表和无序列表的插入

V0.0.4

  1. 增加新的api:XWPFTemplate.compile
  2. 渲染数据除了支持Map以外,还支持JavaBean渲染
  3. 升级poi组件至最新版本3.16

V0.0.3

  1. 新增表单语法#
  2. 支持表单插入
  3. 渲染器支持对table动态处理DynamicTableRenderPolicy
  4. 支持单元格的合并
  5. 丰富文本样式

Office Open XML -- OOXML

为了让微软的office用其他软件打开不会出现错乱的问题,所以出现了docx格式的文档(xlsx,pptx也是)。微软的OOXML文档格式已被批准为全球行业标准。 如果要了解比apache poi 更低层的ooxml,可以访问office open xml

在office open xml网站里,你会知道文件是怎样实现的,颜色、字体、粗体等是怎么设置的。

docx 文档转成xml后,普通文本就是这个样子的:

<w:r>
    <w:rPr>
        <w:b/>
        <w:i/>
    </w:rPr>
    <w:t>我是文本</w:t>
</w:r>

设置文本为粗体:

<w:r>
    <w:rPr>
        <w:b w:val="true"/>
        <w:i/>
    </w:rPr>
    <w:t>我是文本</w:t>
</w:r>

基本所有的属性,在office open xml都有详细的解析。

Word文档生成目录问题

我一直在找POI生成目录的方法,Jacob可以实现。如果你知道,麻烦告诉我!谢谢

转载于:https://my.oschina.net/isaac21/blog/1610192

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值