当前位置: 代码网 > it编程>编程语言>Java > Java操作Word文档的全面指南

Java操作Word文档的全面指南

2025年07月06日 Java 我要评论
简介word编程最重要的类是org.apache.poi.xwpf.usermodel.xwpfdocument。涉及的东西十分复杂。而且apache poi操作word的技术非常不成熟。代码中本身有

简介

word编程最重要的类是org.apache.poi.xwpf.usermodel.xwpfdocument。涉及的东西十分复杂。而且apache poi操作word的技术非常不成熟。代码中本身有很多bug。

maven的依赖为

<dependency>
    <groupid>org.apache.poi</groupid>
    <artifactid>poi-ooxml</artifactid>
    <version>5.0.0</version>
</dependency>

以下代码创建一个空word文档。

public class emptyword {

    public static void main(string[] args) throws ioexception {
        xwpfdocument document = new xwpfdocument();
        file file = new file("test.word");
        document.write(new fileoutputstream(file));
    }
}

段落

首先看看段落与字体设置。

假设有需要生成一个一级标题,利用document创建段落。每个段落又有多个run组成。run不能继续拆分,一个run拥有共同的字体。如以下代码创建一个段落:

final xwpfparagraph paragraph = document.createparagraph();
paragraph.setnumilvl(biginteger.valueof(1l));
final xwpfrun run = paragraph.createrun();
run.settext("老了");
run.setfontsize(10);
run.setcolor("ffff00");
run.setfontfamily("宋书");

而段落的大纲级别的设置比较复杂,代码如下

ctppr ppr = paragraph.getctp().getppr();
if (ppr == null) {
    ppr = paragraph.getctp().addnewppr();
}
final ctdecimalnumber ctdecimalnumber = ppr.addnewoutlinelvl();
ctdecimalnumber.setval(biginteger.valueof(1));
ppr.setoutlinelvl(ctdecimalnumber);

这里有一个难懂的概念,什么是ctp。

其效果如下:

页头与页脚

页头与页脚测试时发现生成的页头和页脚只能在word中看到,在wps里看不到。这可能是poi的一个bug。生成页头和页脚都比较简单。

final xwpfdocument document = new xwpfdocument();
final xwpfheader header = document.createheader(headerfootertype.default);
final xwpfparagraph paragraph = header.createparagraph();
final xwpfrun run = paragraph.createrun();
run.settext("我是页头");
run.setfontsize(12);
run.setcolor("ff00ff");
system.out.println(header.gettext());

页脚为:

// 页脚呢
final xwpffooter footer = document.createfooter(headerfootertype.default);
final xwpfparagraph footerparagraph = footer.createparagraph();
final xwpfrun footerparagraphrun = footerparagraph.createrun();
footerparagraphrun.settext("页脚");
footerparagraphrun.setfontsize(12);

完整效果如下:

页码

生成页码的方法比较复杂。但是值得挑战一下。

final xwpffooter footer = document.createfooter(headerfootertype.default);
xwpfparagraph paragraph = footer.createparagraph();
xwpfrun run = paragraph.createrun();
run.settext("第");

run = paragraph.createrun();
ctfldchar ctfldchar = run.getctr().addnewfldchar();
ctfldchar.setfldchartype(stfldchartype.begin);

// 又一段
run = paragraph.createrun();
cttext cttext = run.getctr().addnewinstrtext();
cttext.setstringvalue("page \\* mergeformat");
cttext.setspace(spaceattribute.space.enum.forstring("preserve"));

ctfldchar = run.getctr().addnewfldchar();
ctfldchar.setfldchartype(stfldchartype.end);

run = paragraph.createrun();
run.settext("页 总共");

run = paragraph.createrun();
ctfldchar = run.getctr().addnewfldchar();
ctfldchar.setfldchartype(stfldchartype.begin);

run = paragraph.createrun();
cttext = run.getctr().addnewinstrtext();
cttext.setstringvalue("numpages \\* mergeformat");
cttext.setspace(spaceattribute.space.enum.forstring("preserve"));

ctfldchar = run.getctr().addnewfldchar();
ctfldchar.setfldchartype(stfldchartype.end);

run = paragraph.createrun();
run.settext("页");

同样,兼容word,不兼容wps。

效果如下:

表格

word里插入表格,是非常常见的功能。

final xwpfdocument document = new xwpfdocument();
final xwpftable table = document.createtable(3, 3);
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        table.getrow(i).getcell(j).settext(i+"-"+j);
    }
}

表格有点丑,但是勉强可以用哈:

图片

插入图片也是必要的功能啊,代码示例如下:

string imagepath = "image.png"; // 图片路径
            fileinputstream imagestream = new fileinputstream(imagepath);

            // 设置图片尺寸(单位:emu)
            int width = units.toemu(300); // 宽度(约4厘米)
            int height = units.toemu(200); // 高度

            final xwpfparagraph paragraph = document.createparagraph();
            final xwpfrun run = paragraph.createrun();
            // 插入图片
            run.addpicture(
                    imagestream,
                    xwpfdocument.picture_type_png, // 图片格式
                    "image.png", // 描述文本
                    width,
                    height
            );
            imagestream.close();

插入图片效果:

批注

word编程加批注是十分困难的、十分复杂的。在poi里,有同名的包,不能导错,以下是正确的包:

import org.openxmlformats.schemas.wordprocessingml.x2006.main.ctcomment;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.ctcomments;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.cttext;

首先要写一个辅助类,辅助类倒是比较简单:

public class myxwpfcommentsdocument extends poixmldocumentpart {

    private ctcomments ctcomments;

    private myxwpfcommentsdocument(packagepart part) {
        super(part);
        ctcomments = commentsdocument.factory.newinstance().addnewcomments();
    }

    public ctcomments getctcomments() {
        return ctcomments;
    }

    @override
    protected void commit() throws ioexception {
        xmloptions xmloptions = new xmloptions(poixmltypeloader.default_xml_options);
        xmloptions.setsavesyntheticdocumentelement(new qname(ctcomments.type.getname().getnamespaceuri(), "comments"));
        packagepart part = getpackagepart();
        outputstream out = part.getoutputstream();
        ctcomments.save(out, xmloptions);
        out.close();
    }

    public static myxwpfcommentsdocument createcommentsdocument(xwpfdocument document) throws exception {
        opcpackage opcpackage = document.getpackage();
        packagepartname partname = packagingurihelper.createpartname("/word/comments.xml");
        packagepart part = opcpackage.createpart(partname, "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml");
        myxwpfcommentsdocument myxwpfcommentsdocument = new myxwpfcommentsdocument(part);

        string rid = "rid" + (document.getrelationparts().size()+1);
        document.addrelation(rid, xwpfrelation.comment, myxwpfcommentsdocument);

        return myxwpfcommentsdocument;
    }
}

以下是加入批注的方法:

 myxwpfcommentsdocument myxwpfcommentsdocument = myxwpfcommentsdocument.createcommentsdocument(document);

        ctcomments comments = myxwpfcommentsdocument.getctcomments();
        ctcomment ctcomment;
        xwpfparagraph paragraph;


        //first comment
        biginteger cid = biginteger.zero;

        ctcomment = comments.addnewcomment();
        cttext cttext = ctcomment.addnewp().addnewr().addnewt();
        cttext.setstringvalue("the first comment.");
        ctcomment.setauthor("axel ríchter");
        ctcomment.setinitials("ar");
        ctcomment.setid(cid);


        paragraph = document.createparagraph();
        paragraph.getctp().addnewcommentrangestart().setid(cid);

        xwpfrun run;
        run = paragraph.createrun();
        run.settext("paragraph with the first comment.");

        paragraph.getctp().addnewcommentrangeend().setid(cid);

        paragraph.getctp().addnewr().addnewcommentreference().setid(cid);

以下是批注的效果:

文本框

文本框的插入也是比较复杂,代码如下:

xwpfparagraph paragraph = document.createparagraph();
xwpfrun run=paragraph.createrun();
run.settext("the body text: ");

ctgroup ctgroup = ctgroup.factory.newinstance();

ctshape ctshape = ctgroup.addnewshape();
ctshape.setstyle("width:100pt;height:24pt");
cttxbxcontent cttxbxcontent = ctshape.addnewtextbox().addnewtxbxcontent();
cttxbxcontent.addnewp().addnewr().addnewt().setstringvalue("the textbox text...");

node ctgroupnode = ctgroup.getdomnode();
ctpicture ctpicture = ctpicture.factory.parse(ctgroupnode);
run=paragraph.createrun();
ctr ctr = run.getctr();
ctr.addnewpict();
ctr.setpictarray(0, ctpicture);

简单展示下效果:

目录

目录本来就一句话doc.createtoc(),但是很容易失败。使用ctppr可以设置段落的大纲级别,以下是代码:

try (xwpfdocument document = new xwpfdocument()) {

            document.createtoc();

            final xwpfparagraph paragraph = document.createparagraph();
            // 获取段落属性,若不存在则新建
            ctppr ppr = paragraph.getctp().issetppr() ? paragraph.getctp().getppr() : paragraph.getctp().addnewppr();

            // 设置大纲级别为 1
            ctdecimalnumber outlinelvl = ppr.issetoutlinelvl() ? ppr.getoutlinelvl() : ppr.addnewoutlinelvl();
            outlinelvl.setval(biginteger.valueof(1));

            final xwpfrun run = paragraph.createrun();
            run.settext("标题一");
            run.setfontsize(10);
            run.setfontfamily("宋书");

            file file = new file("toc.docx");
            document.write(files.newoutputstream(file.topath()));
        }

虽然代码运行不报错,但是结果是生成不了目录。以下是效果图:

图表

英文叫chart,chart是需要关联excel表格的。所以这个特别复杂。完整代码如下:

// create the data
     string[] categories = new string[] { "lang 1", "lang 2", "lang 3" };
     double[] valuesa = new double[] { 10d, 20d, 30d };
     double[] valuesb = new double[] { 15d, 25d, 35d };

     // create the chart
     xwpfchart chart = doc.createchart(15 * units.emu_per_centimeter, 10 * units.emu_per_centimeter);

     // create data sources
     int numofpoints =categories. length;
     string categorydatarange = chart.formatrange(new cellrangeaddress(1, numofpoints, 0, 0));
     string valuesdatarangea = chart.formatrange(new cellrangeaddress(1, numofpoints, 1, 1));
     string valuesdatarangeb = chart.formatrange(new cellrangeaddress(1, numofpoints, 2, 2));
     xddfdatasource<string> categoriesdata = xddfdatasourcesfactory.fromarray(categories, categorydatarange, 0);
     xddfnumericaldatasource<double> valuesdataa = xddfdatasourcesfactory.fromarray(valuesa, valuesdatarangea, 1);
     xddfnumericaldatasource<double> valuesdatab = xddfdatasourcesfactory.fromarray(valuesb, valuesdatarangeb, 2);

     // create axis
     xddfcategoryaxis bottomaxis = chart.createcategoryaxis(axisposition.bottom);
     xddfvalueaxis leftaxis = chart.createvalueaxis(axisposition.left);
     leftaxis.setcrosses(axiscrosses.auto_zero);
     // set axiscrossbetween, so the left axis crosses the category axis between the categories.
     // else first and last category is exactly on cross points and the bars are only half visible.
     leftaxis.setcrossbetween(axiscrossbetween.between);

     // create chart data
     xddfchartdata data = chart.createdata(charttypes.bar, bottomaxis, leftaxis);
     ((xddfbarchartdata) data).setbardirection(bardirection.col);

     // create series
     // if only one series do not vary colors for each bar
     ((xddfbarchartdata) data).setvarycolors(false);
     xddfchartdata.series series = data.addseries(categoriesdata, valuesdataa);
     // xddfchart.setsheettitle is buggy. it creates a table but only half way and incomplete.
     // excel cannot opening the workbook after creatingg that incomplete table.
     // so updating the chart data in word is not possible.
     //series.settitle("a", chart.setsheettitle("a", 1));
     series.settitle("a", settitleindatasheet(chart, "a", 1));

/*
   // if more than one series do vary colors of the series
   ((xddfbarchartdata)data).setvarycolors(true);
   series = data.addseries(categoriesdata, valuesdatab);
   //series.settitle("b", chart.setsheettitle("b", 2));
   series.settitle("b", settitleindatasheet(chart, "b", 2));
*/

     // plot chart data
     chart.plot(data);

     // create legend
     xddfchartlegend legend = chart.getoraddlegend();
     legend.setposition(legendposition.left);
     legend.setoverlay(false);

还有一个私有方法:

static cellreference settitleindatasheet(xwpfchart chart, string title, int column) throws exception {
    xssfworkbook workbook = chart.getworkbook();
    xssfsheet sheet = workbook.getsheetat(0);
    xssfrow row = sheet.getrow(0);
    if (row == null)
        row = sheet.createrow(0);
    xssfcell cell = row.getcell(column);
    if (cell == null)
        cell = row.createcell(column);
    cell.setcellvalue(title);
    return new cellreference(sheet.getsheetname(), 0, column, true, true);
}

运行效果如下:

以上就是java操作word的全面指南的详细内容,更多关于java操作word的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com