【问题标题】:How to create a bulleted list in Apache POI XWPF Document?如何在 Apache POI XWPF 文档中创建项目符号列表?
【发布时间】:2023-03-27 12:37:02
【问题描述】:

我想用 Java 在 docx word 文档中创建一个项目符号/编号列表。我正在使用 Apache POI 3.10 库。如果我理解正确,步骤是这样的:

  1. 创建编号numbering = doc.createNumbering
  2. 将AbstractNum添加到Numbering中,得到对应的abstractNumId
  3. 添加一个带有 AbstractNumId numId = numbering.addNum(abstractNumId) 的 Num
  4. 现在我可以使用 para.setNumID(numId) 将 numId 添加到段落中;

但是我被困在第二步。如何创建可以添加到编号的 AbstractNum 对象?

【问题讨论】:

    标签: java apache-poi


    【解决方案1】:

    我试图做类似的事情,然后用头撞它,直到它开始工作。

    这是我将 AbstractNum 添加到文档编号对象的方法。调用 'addAbstractNum()' 结果在我使用的版本(3.10-FINAL)中有一个空错误。因此,要绕过它,您需要在 XML 中生成 AbstractNum,对其进行解析,手动为其分配一个 id,并将其添加到文档的编号对象中。

    我就是这样做的:

    protected XWPFDocument doc;     
    private BigInteger addListStyle(String style)
    {
        try
        {
            XWPFNumbering numbering = doc.getNumbering();
            // generate numbering style from XML
            CTAbstractNum abstractNum = CTAbstractNum.Factory.parse(style);
            XWPFAbstractNum abs = new XWPFAbstractNum(abstractNum, numbering);
    
            // find available id in document
            BigInteger id = BigInteger.valueOf(0);
            boolean found = false;
            while (!found)
            {
                Object o = numbering.getAbstractNum(id);
                found = (o == null);
                if (!found) id = id.add(BigInteger.ONE);
            }
            // assign id
            abs.getAbstractNum().setAbstractNumId(id);
            // add to numbering, should get back same id
            id = numbering.addAbstractNum(abs);
            // add to num list, result is numid
            return doc.getNumbering().addNum(id);           
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }
    

    传递给方法的“样式”字符串的格式需要 XWPF 文档的知识——我对此一无所知。因此,我创建了一个带有我想要的编号样式的 word 文档。将其保存到“.docx”文件中。解压缩“.docx”文件,并从“numbering.xml”复制 XML 片段。它看起来像这样:

    <xml-fragment xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14"><w:nsid w:val="1656060D" /><w:multiLevelType w:val="hybridMultilevel" /><w:tmpl w:val="99FCFC1A" /><w:lvl w:ilvl="0" w:tplc="0409000F"><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:tplc="04090019" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerLetter" /><w:lvlText w:val="%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:tplc="0409001B" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerRoman" /><w:lvlText w:val="%3." /><w:lvlJc w:val="right" /><w:pPr><w:ind w:left="2160" w:hanging="180" /></w:pPr></w:lvl><w:lvl w:ilvl="3" w:tplc="0409000F" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%4." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="2880" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="4" w:tplc="04090019" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerLetter" /><w:lvlText w:val="%5." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="3600" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="5" w:tplc="0409001B" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerRoman" /><w:lvlText w:val="%6." /><w:lvlJc w:val="right" /><w:pPr><w:ind w:left="4320" w:hanging="180" /></w:pPr></w:lvl><w:lvl w:ilvl="6" w:tplc="0409000F" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%7." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="5040" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="7" w:tplc="04090019" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerLetter" /><w:lvlText w:val="%8." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="5760" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="8" w:tplc="0409001B" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerRoman" /><w:lvlText w:val="%9." /><w:lvlJc w:val="right" /><w:pPr><w:ind w:left="6480" w:hanging="180" /></w:pPr></w:lvl></xml-fragment>
    

    获取该字符串,将其传递给上面的方法。现在你有了一个 numID,你可以用它来制作列表。

    XWPFParagraph para = doc.createParagraph();
    para.setStyle("ListParagraph");
    para.setNumID(listType);
    para.getCTP().getPPr().getNumPr().addNewIlvl().setVal(BigInteger.valueOf(level));
    

    祝你好运。

    【讨论】:

      【解决方案2】:

      我也使用了您提到的相同步骤,对于第二步,我使用了以下语句。

      BigInteger abstractNumId = BigInteger.valueOf(0);
      

      有了这个,我能够创建项目符号列表。但是,我仍然没有找到创建编号列表的方法。

      【讨论】:

        【解决方案3】:

        我发现最简单的方法是从具有所需样式的 Word 文档中提取 numbering.xml,然后使用 Java 从 XML 树中手动重新创建重要标记。这听起来有点痛苦,但它比尝试解析 MS Word 创建的内容要精确得多。 CT 函数是以它们影响的 XML 节点命名的,所以一旦你明白了它就相当直观。

        以我为例(这将为项目符号或编号的单级列表返回正确的CTAbstractNum):

        private static CTAbstractNum getAbstractNumber(STNumberFormat.Enum numFmt) {
        
            CTAbstractNum ctAbsNum = CTAbstractNum.Factory.newInstance();
        
            CTLvl lvl = ctAbsNum.addNewLvl(); //Add a level
        
            CTDecimalNumber start = lvl.addNewStart(); //Set the starting number
            start.setVal(BigInteger.ONE);
        
            CTNumFmt fmt = lvl.addNewNumFmt(); //Set the number format
            fmt.setVal(numFmt);
        
            //Add the text that's used for the bullet point 
            CTLevelText lt = lvl.addNewLvlText(); 
            if (numFmt == STNumberFormat.BULLET) {
                lt.setVal("");     
                lvl.addNewRPr(); //Set the Symbol font
                CTFonts f = lvl.getRPr().addNewRFonts();
                f.setAscii("Symbol");
                f.setHAnsi("Symbol");
            }
            else { //Decimal
                lt.setVal("%1.");
            }
        
            lvl.addNewPPr(); 
            CTInd ind = lvl.getPPr().addNewInd(); //Set the indent
            ind.setHanging(BigInteger.valueOf(360));
            ind.setLeft(BigInteger.valueOf(720));           
        
            System.out.println(ctAbsNum);
        
            return ctAbsNum;
        }
        

        【讨论】:

          【解决方案4】:
          static void addCustomHeadingStyle(XWPFDocument docxDocument, XWPFStyles styles, String strStyleId, int headingLevel, int pointSize, String hexColor) {
                  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));
          
                  // lower number > style is more prominent in the formats bar
                  ctStyle.setUiPriority(indentNumber);
          
                  CTOnOff onoffnull = CTOnOff.Factory.newInstance();
                  ctStyle.setUnhideWhenUsed(onoffnull);
          
                  // style shows up in the formats bar
                  ctStyle.setQFormat(onoffnull);
          
                  // style defines a heading of the given level
                  CTPPr ppr = CTPPr.Factory.newInstance();
                  ppr.setOutlineLvl(indentNumber);
                  ppr.addNewNumPr().addNewNumId().setVal(BigInteger.ONE);
                  ctStyle.setPPr(ppr);
          
                  XWPFStyle style = new XWPFStyle(ctStyle);
          
                  CTHpsMeasure size = CTHpsMeasure.Factory.newInstance();
                  size.setVal(new BigInteger(String.valueOf(pointSize)));
                  CTHpsMeasure size2 = CTHpsMeasure.Factory.newInstance();
                  size2.setVal(new BigInteger("24"));
          
                  CTFonts fonts = CTFonts.Factory.newInstance();
                  fonts.setAscii("Calibri Light");
          
                  CTRPr rpr = CTRPr.Factory.newInstance();
                  rpr.setRFonts(fonts);
                  rpr.setSz(size);
                  rpr.setSzCs(size2);
          
                  style.setType(STStyleType.PARAGRAPH);
                  styles.addStyle(style);
              }
          }
          

          可以添加自定义样式,用于创建标题和索引,这个答案不是我的,只是添加数字的一些更改

          【讨论】:

          • 那么您在哪里得到了答案?这里需要适当的归属。
          • 创建自定义样式后,您可以添加目录列表,并添加自定义样式。
          【解决方案5】:

          打开文档 从文档中获取 XWPFNumbering 对象。 从文档中获取 XWPFParagraphs 列表。 逐步浏览段落列表,并在获得每一个段落时尝试从中获取数字 ID。如果这返回 null 则该段落不在列表中。如果它不返回 null,您可以使用 BigInteger 从 XWPFNumbering 对象中检索应用于段落的编号/项目符号方案的详细信息。

          我来自this website

          【讨论】:

          • 是的,我看到了那个网站,但它只说明了如何检索文档中已经存在的列表项。我想要的是在文档中创建一个新列表。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-11
          相关资源
          最近更新 更多