0

I using doc.createTOC(); to generate table of content in poi word(XWPF), and using addCustomHeadingStyle(doc, ChapterLvl.LVL_1.style, 1); to generate headings Content, dotted line and page number. But I want to set different heading level's headings Content have different font size,font color, bold, and dotted line and page number have same style with its corresponding heading content, like the picture attachment, can someone give me some advice how to do that?

private static void addCustomHeadingStyle(XWPFDocument docxDocument, String strStyleId, int headingLevel) {
        CTStyle ctStyle = CTStyle.Factory.newInstance();
        ctStyle.setStyleId(strStyleId);
        CTString styleName = CTString.Factory.newInstance();
        styleName.setVal(strStyleId);
        ctStyle.setName(styleName);
        CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
        indentNumber.setVal(BigInteger.valueOf(headingLevel));
        ctStyle.setUiPriority(indentNumber);
        CTOnOff onoffnull = CTOnOff.Factory.newInstance();
        ctStyle.setUnhideWhenUsed(onoffnull);
        ctStyle.setQFormat(onoffnull);
        CTPPr ppr = CTPPr.Factory.newInstance();
        ppr.setOutlineLvl(indentNumber);
        ctStyle.setPPr(ppr);
        XWPFStyle style = new XWPFStyle(ctStyle);
        XWPFStyles styles = docxDocument.createStyles();
        style.setType(STStyleType.PARAGRAPH);
        styles.addStyle(style);
    }

enter image description here

Sucy
  • 488
  • 11
  • 19

2 Answers2

1

I even cannot get valuable results using XWPFDocument.createTOC() (fields for page numbers are wrong for example). So I have never used that. What I can say is that XWPFDocument.createTOC() currently not creates a "Table of Contents" like Word itself will do. So after opening in Word it will not be recognized as TOC from Word GUI.

But if the question is only about the styles, then that can be answered. There must be styles named "toc 1", "toc 2", "toc 3", ..., "toc n" present for each heading level. This can be achieved using XML like:

<w:style w:styleId="TOC1" w:type="paragraph">
 <w:name w:val="toc 1"/>
 <w:basedOn w:val="Normal"/>
 <w:next w:val="Normal"/>
 <w:autoRedefine/><w:unhideWhenUsed/>
 <w:rPr>
  <w:b/><w:bCs/><w:caps/><w:sz w:val="32"/><w:szCs w:val="32"/>
 </w:rPr>
</w:style>

The most formatting is done within the rPr (Run Properties) element and it's children.

Example:

import java.io.File;
import java.io.FileOutputStream;

import org.apache.poi.xwpf.usermodel.*;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumbering;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyle;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;

import java.math.BigInteger;

public class CreateWordNumberedHeadings {

 static String cTAbstractNumDecimalXML = 
  "<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=\"decimal\"/><w:lvlText w:val=\"%1\"/><w:lvlJc w:val=\"left\"/><w:pPr><w:ind w:left=\"360\" 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=\"720\" 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=\"1440\" w:hanging=\"360\"/></w:pPr></w:lvl>"
+ "</w:abstractNum>";

 static String cTStyleNormal =
  "<w:style xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:type=\"paragraph\" w:default=\"1\" w:styleId=\"Normal\">" 
+ "<w:name w:val=\"Normal\"/>"
+ "<w:qFormat/>"
+ "</w:style>";
 static String cTStyleH1XML =
  "<w:style xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:type=\"paragraph\" w:styleId=\"Heading1\">"
+ "<w:name w:val=\"heading 1\"/>"
+ "<w:basedOn w:val=\"Normal\"/>"
+ "<w:next w:val=\"Normal\"/>"
+ "<w:unhideWhenUsed/>"
+ "<w:qFormat/>"
+ "<w:rPr><w:b/><w:sz w:val=\"36\"/></w:rPr>"
+ "</w:style>";
 static String cTStyleH2XML =
  "<w:style xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:type=\"paragraph\" w:styleId=\"Heading2\">"
+ "<w:name w:val=\"heading 2\"/>"
+ "<w:basedOn w:val=\"Normal\"/>"
+ "<w:next w:val=\"Normal\"/>"
+ "<w:unhideWhenUsed/>"
+ "<w:qFormat/>"
+ "<w:rPr><w:sz w:val=\"32\"/></w:rPr>"
+ "</w:style>";
 static String cTStyleH3XML =
  "<w:style xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:type=\"paragraph\" w:styleId=\"Heading3\">"
+ "<w:name w:val=\"heading 3\"/>"
+ "<w:basedOn w:val=\"Normal\"/>"
+ "<w:next w:val=\"Normal\"/>"
+ "<w:unhideWhenUsed/>"
+ "<w:qFormat/>"
+ "<w:rPr><w:i/><w:sz w:val=\"28\"/></w:rPr>"
+ "</w:style>";

static String cTStyleTOC1 =
  "<w:style xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:type=\"paragraph\" w:styleId=\"TOC1\">"
+ "<w:name w:val=\"toc 1\"/>"
+ "<w:basedOn w:val=\"Normal\"/>"
+ "<w:next w:val=\"Normal\"/>"
+ "<w:autoRedefine/><w:unhideWhenUsed/>"
+ "<w:rPr><w:b/><w:bCs/><w:caps/><w:color w:val=\"C00000\"/><w:sz w:val=\"32\"/><w:szCs w:val=\"32\"/></w:rPr>"
+ "</w:style>";

static String cTStyleTOC2 =
  "<w:style xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:type=\"paragraph\" w:styleId=\"TOC2\">"
+ "<w:name w:val=\"toc 2\"/>"
+ "<w:basedOn w:val=\"Normal\"/>"
+ "<w:next w:val=\"Normal\"/>"
+ "<w:autoRedefine/><w:unhideWhenUsed/>"
+ "<w:rPr><w:i/><w:iCs/><w:sz w:val=\"28\"/><w:szCs w:val=\"28\"/></w:rPr>"
+ "</w:style>";

 public static void main(String[] args) throws Exception {

  XWPFDocument document = new XWPFDocument();       
        
  XWPFNumbering numbering = document.createNumbering();
  CTNumbering cTNumbering = CTNumbering.Factory.parse(cTAbstractNumDecimalXML);
  CTAbstractNum cTAbstractNum = cTNumbering.getAbstractNumArray(0);
  XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
 
  BigInteger abstractNumID = numbering.addAbstractNum(abstractNum);
  BigInteger numID = numbering.addNum(abstractNumID);

  XWPFStyles styles = document.createStyles();
  CTStyles cTStyles = CTStyles.Factory.parse(cTStyleNormal);
  CTStyle cTStyle = cTStyles.getStyleArray(0);
  styles.addStyle(new XWPFStyle(cTStyle));
  cTStyles = CTStyles.Factory.parse(cTStyleH1XML);
  cTStyle = cTStyles.getStyleArray(0);
  styles.addStyle(new XWPFStyle(cTStyle));
  cTStyles = CTStyles.Factory.parse(cTStyleH2XML);
  cTStyle = cTStyles.getStyleArray(0);
  styles.addStyle(new XWPFStyle(cTStyle));
  cTStyles = CTStyles.Factory.parse(cTStyleH3XML);
  cTStyle = cTStyles.getStyleArray(0);
  styles.addStyle(new XWPFStyle(cTStyle));

  cTStyles = CTStyles.Factory.parse(cTStyleTOC1);
  cTStyle = cTStyles.getStyleArray(0);
  styles.addStyle(new XWPFStyle(cTStyle));
  cTStyles = CTStyles.Factory.parse(cTStyleTOC2);
  cTStyle = cTStyles.getStyleArray(0);
  styles.addStyle(new XWPFStyle(cTStyle));

  createParagraphs(document, numID, 
   new String[] {"First Level",
                 "Second Level",
                 "First Level",
                 "Second Level",
                 "Third Level",
                 "Second Level",
                 "Third Level",
                 "Second Level",
                 "First Level"}
  );

  XWPFParagraph paragraph = document.createParagraph();
  XWPFRun run=paragraph.createRun();
  run.setText("Table of contents:");

  paragraph = document.createParagraph();

  CTSimpleField toc = paragraph.getCTP().addNewFldSimple();
  toc.setInstr("TOC \\* MERGEFORMAT");
  toc.setDirty(STOnOff.TRUE);

  FileOutputStream out = new FileOutputStream("CreateWordNumberedHeadings.docx");   
  document.write(out);
  out.close();
  document.close();

 }

 public static void createParagraphs(XWPFDocument doc, BigInteger numID, String[] content) {
  for (String value : content) {
   XWPFParagraph para = doc.createParagraph();
   para.setVerticalAlignment(TextAlignment.CENTER);
   para.setNumID(numID);
   para.setStyle("Heading1");
   if (value.contains("Second")) {
    para.getCTP().getPPr().getNumPr().addNewIlvl().setVal(BigInteger.valueOf(1));
    para.setStyle("Heading2");
   }
   if(value.contains("Third")) {
    para.getCTP().getPPr().getNumPr().addNewIlvl().setVal(BigInteger.valueOf(2));
    para.setStyle("Heading3");
   }
   XWPFRun run = para.createRun();
   run.setText(value);
   para = doc.createParagraph();
   run = para.createRun();
   run.setText("Lorem ipsum semit dolor ...");
   run.addBreak(BreakType.PAGE); 
  } 
 }
}

Note: I do not use XWPFDocument.createTOC() for mentioned reasons. Instead I am inserting a field which must be refreshed while opening the document in Word. This leads to a confirm dialog while opening which must be answered with Yes.


To make this code work using apache poi versions greater than 4, where the ooxml-schemas library is not used anymore and instead poi-ooxml-full library is used, you need changing the following:

...
//import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
...
  //toc.setDirty(STOnOff.TRUE);
  toc.setDirty(true);
...

In short: Do'nt use STOnOff anymore. Instead do using boolean values.

Axel Richter
  • 56,077
  • 6
  • 60
  • 87
  • `document.createTOC(); XWPFParagraph paragraph = document.createParagraph(); CTSimpleField toc = paragraph.getCTP().addNewFldSimple(); toc.setInstr("TOC \\* MERGEFORMAT"); toc.setDirty(STOnOff.TRUE); createParagraphs(document, numID, "...content...");` Thanks so much ! Your answer is what I want. You cannot get valuable results using XWPFDocument.createTOC(), because you createTOC after your content, this can lead to toc page number larger the real page number, so the bookmark will not be generate links. If you create toc before content that will be ok. As code shown above. – Sucy Apr 18 '17 at 01:24
0

A lot of your questions seem to be about XWPF. This is an incomplete API. You could read the spec to determine how to do things not surfaced in the API, and then potentially donate it back to the community. The specs are here. The Apache POI project uses the first edition schema. I find that Parts 1, 3, and 4 are the most useful.

As to this specific question, are you getting what you want with doc.createTOC()? What are you getting? Is it located in the appropriate place in the document? There are other questions concerning TOC here on SO. This one in particular may help. If you need additional behaviors, the specs mentioned above contain more switches for the TOC field such as custom header styles to be included. The style of the TOC levels can be set by modifying the appropriate TOCx style where x is the TOC level.

Community
  • 1
  • 1
jmarkmurphy
  • 11,030
  • 31
  • 59