使用 POI v5 在 Word 中创建嵌套项目符号列表
Creating nested bullet lists in Word using POI v5
我在 Java 工作,使用以下 Maven 依赖项(没有其他):
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
和以下 class,从另一个 SO post 中收集:
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.poi.xwpf.usermodel.XWPFAbstractNum;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFNumbering;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLvl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STNumberFormat;
public class CreateSimpleWordBulletList
{
public static void main(String[] args) throws Exception
{
CTAbstractNum cTAbstractNum = CTAbstractNum.Factory.newInstance();
// Next we set the AbstractNumId. This requires care.
// Since we are in a new document we can start numbering from 0.
// But if we have an existing document, we must determine the next free
// number first.
cTAbstractNum.setAbstractNumId(BigInteger.valueOf(0));
// Bullet list
CTLvl cTLvl = cTAbstractNum.addNewLvl();
cTLvl.addNewNumFmt().setVal(STNumberFormat.BULLET);
cTLvl.addNewLvlText().setVal("•");
XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
XWPFDocument document = new XWPFDocument();
XWPFNumbering numbering = document.createNumbering();
BigInteger abstractNumID = numbering.addAbstractNum(abstractNum);
BigInteger numID = numbering.addNum(abstractNumID);
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("A list having defined gap between bullet point and text:");
ArrayList<String> documentList = new ArrayList<String>(Arrays.asList(new String[] { "One", "Two", "Three" }));
for (String string : documentList)
{
paragraph = document.createParagraph();
paragraph.setNumID(numID);
// set indents in Twips (twentieth of an inch point, 1440 Twips = 1 inch
paragraph.setIndentFromLeft(1440 / 4); // indent from left 360 Twips = 1/4
// inch
paragraph.setIndentationHanging(1440 / 4); // indentation hanging 360
// Twips = 1/4 inch
// so bullet point hangs 1/4
// inch before the text at
// indentation 0
run = paragraph.createRun();
run.setText(string);
}
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("CreateWordSimplestBulletList.docx");
document.write(out);
out.close();
document.close();
}
}
这会创建一个项目符号列表,如我所愿;我希望缩进它,但那是次要的。
我需要对其进行的真正修改是再添加两级列表,这样在 Word 文档中的结果类似于以下内容:
* One
- AAA
o aaa
o bbb
o ccc
- BBB
o xyz
o abc
* Two
- AAA
o mmm
o nnn
- ZZZ
o bbb
o nnn
等等
我希望不需要有人为此编写代码,但我不理解也找不到任何关于 CTAbstractNum
、CTLvl
或 [=15] 的文档=] classes。如果有足够的文档,那么有人可以指出我。
我从这里的其他评论中收集到 CTAbstractNum.setAbstractNumId()
中设置的值标识文档范围内的编号级别,因此它应该应用于最外层的所有项目,并且从概念上讲,另一个此类 ID 将应用于每个项目符号内同一级别的所有项目(例如,上图中的 'aaa'、'bbb'、'ccc' 字符串)。我猜会创建不同的 ID 并将其应用于每个此类内部列表。但是当我对 API 的概念模型一无所知时,我讨厌猜测那个。试错编程太无聊了。
我已经提供了关于如何创建 Word 编号的多个答案。还介绍了如何创建多级编号。例如:Apache poi multiline bullet point is working but not multiple paragaraph? and .
但是由于您也要求提供文档,我会尽量阐明这一点。
现代 Word 文档 (*.docx
) 使用 Office Open XML 文件格式。该格式最初由 Ecma(如 ECMA-376)标准化,在后来的版本中由 ISO 和 IEC(如 ISO/IEC 29500)标准化。它是一个 ZIP 存档,包含 XML 和特殊目录结构中的其他文件。因此,可以轻松地解压缩 *.docx
文件并查看内部结构。
基于这些标准化,apache
在 org.openxmlformats.schemas
类 中开发了 XML 个 bean。 apache poi
版本 4 之前 类 是在 ooxml-schemas. From apache poi
version 5 on they are in poi-ooxml-full 中发布的。还有一个 poi-ooxml-lite
版本。但这只包含高层 apache poi
类 使用的那些 bean。因此,当涉及到更特殊的用例时,它缺少一些 bean。
很遗憾,org.openxmlformats.schemas
类 public 没有可用的文档。但是当然可以下载源代码并从这些源代码中执行 javadoc
以至少获得 API 文档。
XWPF
是 apache poi
对 Microsoft Word 使用的 Office Open XML 部分的高级实现。它使用 org.openxmlformats.schemas
beans 来实现更方便的方法。它记录在 https://poi.apache.org/: Apache POI - Javadocs, POI-XWPF - A Quick Guide 中。但是 XWPF
并未包含 Microsoft Word 迄今为止的所有可能性和功能。因此对于一些特殊的用例,需要了解 XML 和 org.openxmlformats.schemas
bean 的用法。
由于 *.docx
只是一个 ZIP 存档,最方便的方法是使用 Microsoft Word 本身创建一个简单的 *.docx
文件,然后解压缩 *.docx
以获得 XML 已创建。然后尝试使用 XWPF
和 org.openxmlformats.schemas
bean 重新创建 XML。
当涉及到编号时,人们会发现 *.docx
ZIP 存档中有一个 /word/numbering.xml
,其中包含用于编号定义的 XML。每个定义都包含一个 abstractNum
,其中甚至包含对编号的多个缩进级别的定义,以及一个链接到 abstractNum
的 num
。 num
有一个 numId
,它在 /word/document.xml
中用于标记枚举中包含的那些段落。这些段落也可能有一个 ilv
(缩进级别),表示它们在该枚举中的缩进深度。
XWPF
没有完全提供创建abstractNum
中编号的所有功能。它提供了 XWPFAbstractNum
,它有一个采用 org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum
的构造函数。因此需要使用低级 bean 创建 CTAbstractNum
。最简单的方法是从 String
给出的 XML 创建它。 XML 可以通过使用 Microsoft Word 本身创建一个具有编号的简单 *.docx
文件,然后解压缩 *.docx
ZIP 存档来获得。
如果有人能读懂 XML 那么这个 XML 将不言自明。这些元素的名称很好。需要知道的是,缩进和悬挂的度量单位是twips
(二十分之一英寸点)。并且有时用于要点的符号来自额外的 Windows 字体 Symbol and/or Wingdings。那些字体也需要在 XML 然后设置。这些值是 ASCII 值,它使用那些特殊字体的特殊字形。
以下完整示例说明了这一点。它一次创建您显示的枚举作为项目符号列表,一次作为编号列表。
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumbering;
import java.math.BigInteger;
import java.util.Map;
import java.util.TreeMap;
public class CreateWordMultilevelLists {
static String cTAbstractNumBulletXML =
"<w:abstractNum xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:abstractNumId=\"0\">"
+ "<w:multiLevelType w:val=\"hybridMultilevel\"/>"
+ "<w:lvl w:ilvl=\"0\"><w:start w:val=\"1\"/><w:numFmt w:val=\"bullet\"/><w:lvlText w:val=\"\uF0B7\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"720\" w:hanging=\"360\"/></w:pPr><w:rPr><w:rFonts w:ascii=\"Symbol\" w:hAnsi=\"Symbol\" w:hint=\"default\"/></w:rPr></w:lvl>"
+ "<w:lvl w:ilvl=\"1\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"bullet\"/><w:lvlText w:val=\"\u2013\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"1440\" w:hanging=\"360\"/></w:pPr><w:rPr><w:rFonts w:ascii=\"Courier New\" w:hAnsi=\"Courier New\" w:cs=\"Courier New\" w:hint=\"default\"/></w:rPr></w:lvl>"
+ "<w:lvl w:ilvl=\"2\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"bullet\"/><w:lvlText w:val=\"\u26Ac\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"2160\" w:hanging=\"360\"/></w:pPr><w:rPr><w:rFonts w:ascii=\"Courier New\" w:hAnsi=\"Courier New\" w:hint=\"default\"/></w:rPr></w:lvl>"
+ "</w:abstractNum>";
static String cTAbstractNumDecimalXML =
"<w:abstractNum xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:abstractNumId=\"1\">"
+ "<w:multiLevelType w:val=\"hybridMultilevel\"/>"
+ "<w:lvl w:ilvl=\"0\"><w:start w:val=\"1\"/><w:numFmt w:val=\"decimal\"/><w:lvlText w:val=\"%1\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"720\" w:hanging=\"360\"/></w:pPr></w:lvl>"
+ "<w:lvl w:ilvl=\"1\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"decimal\"/><w:lvlText w:val=\"%1.%2\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"1440\" w:hanging=\"360\"/></w:pPr></w:lvl>"
+ "<w:lvl w:ilvl=\"2\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"decimal\"/><w:lvlText w:val=\"%1.%2.%3\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"2160\" w:hanging=\"360\"/></w:pPr></w:lvl>"
+ "</w:abstractNum>";
static BigInteger createNumbering(XWPFDocument document, String abstractNumXML) throws Exception {
CTNumbering cTNumbering = CTNumbering.Factory.parse(abstractNumXML);
CTAbstractNum cTAbstractNum = cTNumbering.getAbstractNumArray(0);
XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
XWPFNumbering numbering = document.createNumbering();
BigInteger abstractNumID = numbering.addAbstractNum(abstractNum);
BigInteger numID = numbering.addNum(abstractNumID);
return numID;
}
static void setIndentLevel(XWPFParagraph paragraph, BigInteger level) {
if (paragraph.getCTP().isSetPPr()) {
if (paragraph.getCTP().getPPr().isSetNumPr()) {
if (paragraph.getCTP().getPPr().getNumPr().isSetIlvl()) {
paragraph.getCTP().getPPr().getNumPr().getIlvl().setVal(level);
} else {
paragraph.getCTP().getPPr().getNumPr().addNewIlvl().setVal(level);
}
}
}
}
static BigInteger getIndentLevelFromNumberingString(String numberingString) {
String[] levels = numberingString.split("\.");
int level = levels.length -1;
return BigInteger.valueOf(level);
}
static void insertListContent(XWPFDocument document, TreeMap<String, String> listContent, BigInteger numID) {
for (Map.Entry<String, String> entry : listContent.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
XWPFParagraph paragraph = document.createParagraph();
paragraph.setNumID(numID);
setIndentLevel(paragraph, getIndentLevelFromNumberingString(key));
XWPFRun run = paragraph.createRun();
run.setText(value);
if (!entry.equals(listContent.lastEntry())) paragraph.setSpacingAfter(0);
}
}
public static void main(String[] args) throws Exception {
TreeMap<String, String> listContent = new TreeMap<String, String>();
listContent.put("1", "One");
listContent.put("1.1", "AAA");
listContent.put("1.1.1", "aaa");
listContent.put("1.1.2", "bbb");
listContent.put("1.1.3", "ccc");
listContent.put("1.2", "BBB");
listContent.put("1.2.1", "xyz");
listContent.put("1.2.2", "abc");
listContent.put("2", "Two");
listContent.put("2.1", "AAA");
listContent.put("2.1.1", "mmm");
listContent.put("2.1.2", "nnn");
listContent.put("2.2", "ZZZ");
listContent.put("2.2.1", "bbb");
listContent.put("2.2.2", "nnn");
XWPFDocument document = new XWPFDocument();
BigInteger numIDBulletList = createNumbering(document, cTAbstractNumBulletXML);
BigInteger numIDDecimalList = createNumbering(document, cTAbstractNumDecimalXML);
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The bullet list:");
insertListContent(document, listContent, numIDBulletList);
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("Paragraph after the list.");
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("The decimal list:");
insertListContent(document, listContent, numIDDecimalList);
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("Paragraph after the list.");
FileOutputStream out = new FileOutputStream("./CreateWordMultilevelLists.docx");
document.write(out);
out.close();
document.close();
}
}
我在 Java 工作,使用以下 Maven 依赖项(没有其他):
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
和以下 class,从另一个 SO post 中收集:
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.poi.xwpf.usermodel.XWPFAbstractNum;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFNumbering;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLvl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STNumberFormat;
public class CreateSimpleWordBulletList
{
public static void main(String[] args) throws Exception
{
CTAbstractNum cTAbstractNum = CTAbstractNum.Factory.newInstance();
// Next we set the AbstractNumId. This requires care.
// Since we are in a new document we can start numbering from 0.
// But if we have an existing document, we must determine the next free
// number first.
cTAbstractNum.setAbstractNumId(BigInteger.valueOf(0));
// Bullet list
CTLvl cTLvl = cTAbstractNum.addNewLvl();
cTLvl.addNewNumFmt().setVal(STNumberFormat.BULLET);
cTLvl.addNewLvlText().setVal("•");
XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
XWPFDocument document = new XWPFDocument();
XWPFNumbering numbering = document.createNumbering();
BigInteger abstractNumID = numbering.addAbstractNum(abstractNum);
BigInteger numID = numbering.addNum(abstractNumID);
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("A list having defined gap between bullet point and text:");
ArrayList<String> documentList = new ArrayList<String>(Arrays.asList(new String[] { "One", "Two", "Three" }));
for (String string : documentList)
{
paragraph = document.createParagraph();
paragraph.setNumID(numID);
// set indents in Twips (twentieth of an inch point, 1440 Twips = 1 inch
paragraph.setIndentFromLeft(1440 / 4); // indent from left 360 Twips = 1/4
// inch
paragraph.setIndentationHanging(1440 / 4); // indentation hanging 360
// Twips = 1/4 inch
// so bullet point hangs 1/4
// inch before the text at
// indentation 0
run = paragraph.createRun();
run.setText(string);
}
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("CreateWordSimplestBulletList.docx");
document.write(out);
out.close();
document.close();
}
}
这会创建一个项目符号列表,如我所愿;我希望缩进它,但那是次要的。
我需要对其进行的真正修改是再添加两级列表,这样在 Word 文档中的结果类似于以下内容:
* One
- AAA
o aaa
o bbb
o ccc
- BBB
o xyz
o abc
* Two
- AAA
o mmm
o nnn
- ZZZ
o bbb
o nnn
等等
我希望不需要有人为此编写代码,但我不理解也找不到任何关于 CTAbstractNum
、CTLvl
或 [=15] 的文档=] classes。如果有足够的文档,那么有人可以指出我。
我从这里的其他评论中收集到 CTAbstractNum.setAbstractNumId()
中设置的值标识文档范围内的编号级别,因此它应该应用于最外层的所有项目,并且从概念上讲,另一个此类 ID 将应用于每个项目符号内同一级别的所有项目(例如,上图中的 'aaa'、'bbb'、'ccc' 字符串)。我猜会创建不同的 ID 并将其应用于每个此类内部列表。但是当我对 API 的概念模型一无所知时,我讨厌猜测那个。试错编程太无聊了。
我已经提供了关于如何创建 Word 编号的多个答案。还介绍了如何创建多级编号。例如:Apache poi multiline bullet point is working but not multiple paragaraph? and
但是由于您也要求提供文档,我会尽量阐明这一点。
现代 Word 文档 (*.docx
) 使用 Office Open XML 文件格式。该格式最初由 Ecma(如 ECMA-376)标准化,在后来的版本中由 ISO 和 IEC(如 ISO/IEC 29500)标准化。它是一个 ZIP 存档,包含 XML 和特殊目录结构中的其他文件。因此,可以轻松地解压缩 *.docx
文件并查看内部结构。
基于这些标准化,apache
在 org.openxmlformats.schemas
类 中开发了 XML 个 bean。 apache poi
版本 4 之前 类 是在 ooxml-schemas. From apache poi
version 5 on they are in poi-ooxml-full 中发布的。还有一个 poi-ooxml-lite
版本。但这只包含高层 apache poi
类 使用的那些 bean。因此,当涉及到更特殊的用例时,它缺少一些 bean。
很遗憾,org.openxmlformats.schemas
类 public 没有可用的文档。但是当然可以下载源代码并从这些源代码中执行 javadoc
以至少获得 API 文档。
XWPF
是 apache poi
对 Microsoft Word 使用的 Office Open XML 部分的高级实现。它使用 org.openxmlformats.schemas
beans 来实现更方便的方法。它记录在 https://poi.apache.org/: Apache POI - Javadocs, POI-XWPF - A Quick Guide 中。但是 XWPF
并未包含 Microsoft Word 迄今为止的所有可能性和功能。因此对于一些特殊的用例,需要了解 XML 和 org.openxmlformats.schemas
bean 的用法。
由于 *.docx
只是一个 ZIP 存档,最方便的方法是使用 Microsoft Word 本身创建一个简单的 *.docx
文件,然后解压缩 *.docx
以获得 XML 已创建。然后尝试使用 XWPF
和 org.openxmlformats.schemas
bean 重新创建 XML。
当涉及到编号时,人们会发现 *.docx
ZIP 存档中有一个 /word/numbering.xml
,其中包含用于编号定义的 XML。每个定义都包含一个 abstractNum
,其中甚至包含对编号的多个缩进级别的定义,以及一个链接到 abstractNum
的 num
。 num
有一个 numId
,它在 /word/document.xml
中用于标记枚举中包含的那些段落。这些段落也可能有一个 ilv
(缩进级别),表示它们在该枚举中的缩进深度。
XWPF
没有完全提供创建abstractNum
中编号的所有功能。它提供了 XWPFAbstractNum
,它有一个采用 org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum
的构造函数。因此需要使用低级 bean 创建 CTAbstractNum
。最简单的方法是从 String
给出的 XML 创建它。 XML 可以通过使用 Microsoft Word 本身创建一个具有编号的简单 *.docx
文件,然后解压缩 *.docx
ZIP 存档来获得。
如果有人能读懂 XML 那么这个 XML 将不言自明。这些元素的名称很好。需要知道的是,缩进和悬挂的度量单位是twips
(二十分之一英寸点)。并且有时用于要点的符号来自额外的 Windows 字体 Symbol and/or Wingdings。那些字体也需要在 XML 然后设置。这些值是 ASCII 值,它使用那些特殊字体的特殊字形。
以下完整示例说明了这一点。它一次创建您显示的枚举作为项目符号列表,一次作为编号列表。
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumbering;
import java.math.BigInteger;
import java.util.Map;
import java.util.TreeMap;
public class CreateWordMultilevelLists {
static String cTAbstractNumBulletXML =
"<w:abstractNum xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:abstractNumId=\"0\">"
+ "<w:multiLevelType w:val=\"hybridMultilevel\"/>"
+ "<w:lvl w:ilvl=\"0\"><w:start w:val=\"1\"/><w:numFmt w:val=\"bullet\"/><w:lvlText w:val=\"\uF0B7\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"720\" w:hanging=\"360\"/></w:pPr><w:rPr><w:rFonts w:ascii=\"Symbol\" w:hAnsi=\"Symbol\" w:hint=\"default\"/></w:rPr></w:lvl>"
+ "<w:lvl w:ilvl=\"1\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"bullet\"/><w:lvlText w:val=\"\u2013\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"1440\" w:hanging=\"360\"/></w:pPr><w:rPr><w:rFonts w:ascii=\"Courier New\" w:hAnsi=\"Courier New\" w:cs=\"Courier New\" w:hint=\"default\"/></w:rPr></w:lvl>"
+ "<w:lvl w:ilvl=\"2\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"bullet\"/><w:lvlText w:val=\"\u26Ac\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"2160\" w:hanging=\"360\"/></w:pPr><w:rPr><w:rFonts w:ascii=\"Courier New\" w:hAnsi=\"Courier New\" w:hint=\"default\"/></w:rPr></w:lvl>"
+ "</w:abstractNum>";
static String cTAbstractNumDecimalXML =
"<w:abstractNum xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:abstractNumId=\"1\">"
+ "<w:multiLevelType w:val=\"hybridMultilevel\"/>"
+ "<w:lvl w:ilvl=\"0\"><w:start w:val=\"1\"/><w:numFmt w:val=\"decimal\"/><w:lvlText w:val=\"%1\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"720\" w:hanging=\"360\"/></w:pPr></w:lvl>"
+ "<w:lvl w:ilvl=\"1\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"decimal\"/><w:lvlText w:val=\"%1.%2\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"1440\" w:hanging=\"360\"/></w:pPr></w:lvl>"
+ "<w:lvl w:ilvl=\"2\" w:tentative=\"1\"><w:start w:val=\"1\"/><w:numFmt w:val=\"decimal\"/><w:lvlText w:val=\"%1.%2.%3\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"2160\" w:hanging=\"360\"/></w:pPr></w:lvl>"
+ "</w:abstractNum>";
static BigInteger createNumbering(XWPFDocument document, String abstractNumXML) throws Exception {
CTNumbering cTNumbering = CTNumbering.Factory.parse(abstractNumXML);
CTAbstractNum cTAbstractNum = cTNumbering.getAbstractNumArray(0);
XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
XWPFNumbering numbering = document.createNumbering();
BigInteger abstractNumID = numbering.addAbstractNum(abstractNum);
BigInteger numID = numbering.addNum(abstractNumID);
return numID;
}
static void setIndentLevel(XWPFParagraph paragraph, BigInteger level) {
if (paragraph.getCTP().isSetPPr()) {
if (paragraph.getCTP().getPPr().isSetNumPr()) {
if (paragraph.getCTP().getPPr().getNumPr().isSetIlvl()) {
paragraph.getCTP().getPPr().getNumPr().getIlvl().setVal(level);
} else {
paragraph.getCTP().getPPr().getNumPr().addNewIlvl().setVal(level);
}
}
}
}
static BigInteger getIndentLevelFromNumberingString(String numberingString) {
String[] levels = numberingString.split("\.");
int level = levels.length -1;
return BigInteger.valueOf(level);
}
static void insertListContent(XWPFDocument document, TreeMap<String, String> listContent, BigInteger numID) {
for (Map.Entry<String, String> entry : listContent.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
XWPFParagraph paragraph = document.createParagraph();
paragraph.setNumID(numID);
setIndentLevel(paragraph, getIndentLevelFromNumberingString(key));
XWPFRun run = paragraph.createRun();
run.setText(value);
if (!entry.equals(listContent.lastEntry())) paragraph.setSpacingAfter(0);
}
}
public static void main(String[] args) throws Exception {
TreeMap<String, String> listContent = new TreeMap<String, String>();
listContent.put("1", "One");
listContent.put("1.1", "AAA");
listContent.put("1.1.1", "aaa");
listContent.put("1.1.2", "bbb");
listContent.put("1.1.3", "ccc");
listContent.put("1.2", "BBB");
listContent.put("1.2.1", "xyz");
listContent.put("1.2.2", "abc");
listContent.put("2", "Two");
listContent.put("2.1", "AAA");
listContent.put("2.1.1", "mmm");
listContent.put("2.1.2", "nnn");
listContent.put("2.2", "ZZZ");
listContent.put("2.2.1", "bbb");
listContent.put("2.2.2", "nnn");
XWPFDocument document = new XWPFDocument();
BigInteger numIDBulletList = createNumbering(document, cTAbstractNumBulletXML);
BigInteger numIDDecimalList = createNumbering(document, cTAbstractNumDecimalXML);
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The bullet list:");
insertListContent(document, listContent, numIDBulletList);
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("Paragraph after the list.");
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("The decimal list:");
insertListContent(document, listContent, numIDDecimalList);
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("Paragraph after the list.");
FileOutputStream out = new FileOutputStream("./CreateWordMultilevelLists.docx");
document.write(out);
out.close();
document.close();
}
}