我尝试使用Apache POI XWPF库在Word docx文件中生成报告。
我的方法是使用现有的Word文档作为样式模板。在模板中,我定义了一个名为" SRINumberList"。
的样式所以要加载模板并删除页眉或页脚中没有的所有内容:
protected void createDocFromTemplate() {
try {
document = new XWPFDocument(this.getClass().getResourceAsStream(styleTemplate));
int pos = document.getBodyElements().size()-1;
while (pos >= 0) {
IBodyElement element = document.getBodyElements().get(pos);
if (!EnumSet.of(BodyType.HEADER, BodyType.FOOTER).contains(element.getPartType())) {
boolean success = document.removeBodyElement(pos);
logger.log(Level.INFO, "Removed body element "+pos+": "+success);
}
pos--;
}
} catch (IOException e) {
logger.log(Level.WARNING, "Not able to load style template", e);
document = new XWPFDocument();
}
}
现在,在我的文档中,有几个不同的部分包含编号列表。每个应该从1重新开始编号。这是我这样做的典型方式:
if (itemStem.getItems().size() > 0) {
p = document.createParagraph();
p.setStyle(ParaStyle.StemAndItemTitle.styleId);
final BigInteger bulletNum = newBulletNumber();
run = p.createRun();
run.setText("Sub Items");
itemStem.getItems().stream().forEach(item -> {
XWPFParagraph p2 = document.createParagraph();
p2.setStyle(ParaStyle.NumberList.styleId);
XWPFRun run2 = p2.createRun();
run2.setText(item.getSubItemText());
});
p = document.createParagraph();
p.createRun();
}
所以这正确地应用了包含数字格式的Style,但是只有一个序列(1 ...在doc中退出了很多列表项)。例如:
Heading 1
1. item a
2. item b
3. item c
Heading 2
4. item a
5. item d
6. item g
但我想要的是:
Heading 1
1. item a
2. item b
3. item c
Heading 2
1. item a
2. item d
3. item g
所以基本上我试图弄清楚如何使用我所拥有的风格,但重新启动页面编号文档中的各个位置。有人可以提供一个如何工作的样本吗?
答案 0 :(得分:0)
我找到的唯一方法是覆盖CTNum中的级别。另一种方法可能是创建大量新的抽象编号/样式,但是当您打开文档时,这将花费大量样式条目。
ArrayList<String> list = new ArrayList<String>();
list.add("SubItem 1");
list.add("SubItem 2");
list.add("SubItem 3");
XWPFNumbering numbering = document.getNumbering();
XWPFAbstractNum numAbstract = numbering.getAbstractNum(BigInteger.ONE);
for (Integer nx = 1; nx < 3; nx++) {
XWPFParagraph p = document.createParagraph();
XWPFRun run = p.createRun();
run.setText("Items " + nx.toString());
//leveloverride (start the new numbering)
BigInteger numId = numbering.addNum(numAbstract.getAbstractNum().getAbstractNumId());
XWPFNum num = numbering.getNum(numId);
CTNumLvl lvloverride = num.getCTNum().addNewLvlOverride();
lvloverride.setIlvl(BigInteger.ZERO);
CTDecimalNumber number = lvloverride.addNewStartOverride();
number.setVal(BigInteger.ONE);
for (String item : list) {
XWPFParagraph p2 = document.createParagraph();
p2.setNumID(num.getCTNum().getNumId());
CTNumPr numProp = p2.getCTP().getPPr().getNumPr();
numProp.addNewIlvl().setVal(BigInteger.ZERO);
XWPFRun run2 = p2.createRun();
run2.setText(item);
}
}
答案 1 :(得分:0)
在keil的帮助下。我找到了解决方案。我在这里发布了一个完整的工作示例:https://github.com/jimklo/apache-poi-sample
诀窍是,在创建重新开始编号的新Num时,需要引用文档中定义的Numbering样式的AbstractNum。
以下是重点,但关键是必须确定文档中Style的AbstractNum ID。这似乎是不幸的,因为这只是一个XML文档,没有一些方法可以枚举现有的Num和AbstractNum。如果有,我很想知道如何做到这一点。
/**
* first discover all the numbering styles defined in the template.
* a bit brute force since I can't find a way to just enumerate all the
* abstractNum's inside the numbering.xml
*/
protected void initNumberingStyles() {
numbering = document.getNumbering();
BigInteger curIdx = BigInteger.ONE;
XWPFAbstractNum abstractNum;
while ((abstractNum = numbering.getAbstractNum(curIdx)) != null) {
if (abstractNum != null) {
CTString pStyle = abstractNum.getCTAbstractNum().getLvlArray(0).getPStyle();
if (pStyle != null) {
numberStyles.put(pStyle.getVal(), abstractNum);
}
}
curIdx = curIdx.add(BigInteger.ONE);
}
}
现在我们有了从Style到AbstractNum的映射,我们可以创建一个通过LvlOverride和StartOverride重新启动的新Num。
/**
* This creates a new num based upon the specified numberStyle
* @param numberStyle
* @return
*/
private XWPFNum restartNumbering(String numberStyle) {
XWPFAbstractNum abstractNum = numberStyles.get(numberStyle);
BigInteger numId = numbering.addNum(abstractNum.getAbstractNum().getAbstractNumId());
XWPFNum num = numbering.getNum(numId);
CTNumLvl lvlOverride = num.getCTNum().addNewLvlOverride();
lvlOverride.setIlvl(BigInteger.ZERO);
CTDecimalNumber number = lvlOverride.addNewStartOverride();
number.setVal(BigInteger.ONE);
return num;
}
现在你可以将NumID应用到你正在创建的列表中。
/**
* This creates a five item list with a simple heading, using the specified style..
* @param index
* @param styleName
*/
protected void createStyledNumberList(int index, String styleName) {
XWPFParagraph p = document.createParagraph();
XWPFRun run = p.createRun();
run.setText(String.format("List %d: - %s", index, styleName));
// restart numbering
XWPFNum num = restartNumbering(styleName);
for (int i=1; i<=5; i++) {
XWPFParagraph p2 = document.createParagraph();
// set the style for this paragraph
p2.setStyle(styleName);
// set numbering for paragraph
p2.setNumID(num.getCTNum().getNumId());
CTNumPr numProp = p2.getCTP().getPPr().getNumPr();
numProp.addNewIlvl().setVal(BigInteger.ZERO);
// set the text
XWPFRun run2 = p2.createRun();
run2.setText(String.format("Item #%d using '%s' style.", i, styleName));
}
// some whitespace
p = document.createParagraph();
p.createRun();
}
同样,总的来说,如果没有keil提供的指针,我就不会想到这一点。