美文网首页我爱编程
Java 对Word文件的生成(基于Apache POI)

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

作者: issac49 | 来源:发表于2018-01-21 00:28 被阅读0次

    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可以实现。如果你知道,麻烦告诉我!谢谢

    相关文章

      网友评论

        本文标题:Java 对Word文件的生成(基于Apache POI)

        本文链接:https://www.haomeiwen.com/subject/illxaxtx.html