Page 1 of 1

Numbering Part missing values

PostPosted: Sat Apr 29, 2023 2:15 am
by petefc
Hi,

We are periodically getting files that error when we try to process them due to missing list references.
The error message is like :"message": "0 level missing for abstractListDefinition 5"

So far all these cases have been for list that are not actually used in the document.

Is there any existing functionality to clean up the list definitions? They are are often filled with many unused lists.

thank

Re: Numbering Part missing values

PostPosted: Mon May 01, 2023 10:56 am
by jason
petefc wrote:We are periodically getting files that error when we try to process them due to missing list references.
The error message is like :"message": "0 level missing for abstractListDefinition 5"


This happens in class ListNumberingDefinition; an override is trying to override a list level for which there is no definition.

Do you happen to know whether the documents in question were created in Word, or some other way?

Could you please post the contents of the relevant abstractListDefinition?

For cleaning up unused lists, you could try:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
package org.docx4j.samples;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.docx4j.Docx4J;
import org.docx4j.TraversalUtil;
import org.docx4j.TraversalUtil.CallbackImpl;
import org.docx4j.jaxb.Context;
import org.docx4j.model.styles.Node;
import org.docx4j.model.styles.StyleTree;
import org.docx4j.model.styles.StyleTree.AugmentedStyle;
import org.docx4j.model.styles.Tree;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.Filetype;
import org.docx4j.openpackaging.packages.OpcPackage;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.Part;
import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.Body;
import org.docx4j.wml.CTEndnotes;
import org.docx4j.wml.CTFootnotes;
import org.docx4j.wml.Comments;
import org.docx4j.wml.Ftr;
import org.docx4j.wml.Hdr;
import org.docx4j.wml.Numbering;
import org.docx4j.wml.Numbering.AbstractNum;
import org.docx4j.wml.Numbering.Num;
import org.docx4j.wml.P;
import org.docx4j.wml.PPr;
import org.docx4j.wml.PPrBase.NumPr;
import org.docx4j.wml.Style;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Remove unused numbering from an output docx
 * (TODO: this version removes it even if it is used in an unused style)
 */

public class RemoveUnusedNumbering {
       
        protected static Logger log = LoggerFactory.getLogger(RemoveUnusedNumbering.class);    
       
        public static void main(String[] args) throws Docx4JException {
               
                WordprocessingMLPackage opcPackage = (WordprocessingMLPackage)OpcPackage.load(
                                new java.io.File(
                                                System.getProperty("user.dir")+ "/OUT_MergeWholeDocumentsIncremental.docx"), Filetype.ZippedPackage);
               
                RemoveUnusedNumbering trimmer = new RemoveUnusedNumbering();
                trimmer.trim(opcPackage);
               
                // Result?
                System.out.println(opcPackage.getMainDocumentPart().getNumberingDefinitionsPart().getXML());
               
                Docx4J.save(opcPackage, new java.io.File(
                                                System.getProperty("user.dir")+ "/OUT_MergeWholeDocumentsIncremental_reduced.docx"));
        }
       
        public void trim(WordprocessingMLPackage opcPackage) throws Docx4JException {
               
                Numbering nIn = opcPackage.getMainDocumentPart().getNumberingDefinitionsPart().getContents();
               
                // Find which styles are used in the docx, across 4 parts
                //Set<String> stylesInUse = opcPackage.getMainDocumentPart().getStylesInUse();
                StyleTree styleTree = opcPackage.getMainDocumentPart().getStyleTree();
                Tree<AugmentedStyle> paragraphStylesTree = styleTree.getParagraphStylesTree();
               
                // Find which numbers are used in the docx,
                // directly or via styles              
                Set<BigInteger> numId = new HashSet<BigInteger>();
               
                // .. via styles
                walk(paragraphStylesTree.getRootElement(), numId);
               
                // .. directly
                getNumberingInUse(opcPackage.getMainDocumentPart(), numId);
               
                // Remove unused numbering
                deleteUnusedNumbering(opcPackage, numId);
               
        }
       
         private static void deleteUnusedNumbering(WordprocessingMLPackage output, Set<BigInteger> usedNumId) throws Docx4JException {

                 Numbering nIn = output.getMainDocumentPart().getNumberingDefinitionsPart().getContents();
                 System.out.println("BEFORE");
         System.out.println("AbsNum: " + nIn.getAbstractNum().size());
         System.out.println("Num: " + nIn.getNum().size());

         System.out.println("keeping: " + usedNumId.size());
         
                 Numbering nOut = Context.getWmlObjectFactory().createNumbering();

                 HashMap<BigInteger, AbstractNum > abstracts = new HashMap<BigInteger, AbstractNum >();
                 for (AbstractNum abstractNum : nIn.getAbstractNum()) {
                         abstracts.put(abstractNum.getAbstractNumId(), abstractNum);
                 }

                 Set<BigInteger > usedAbstracts = new HashSet<BigInteger >();
                 
                 
                 // Copy used Num entries to nOut
                 for(Num num: nIn.getNum()) {
                         if (usedNumId.contains(num.getNumId())) {
                                 // retain
                         log.debug("Keeping NumId " + num.getNumId());                         
                                 nOut.getNum().add(num);
                                 
                                 usedAbstracts.add(num.getAbstractNumId().getVal());
                         }
                 }
                 // Copy used abstractNum entries to nOut
                 for (BigInteger n : usedAbstracts) {
                 log.debug("Keeping abstractNum " + n);                        
                         nOut.getAbstractNum().add(abstracts.get(n));                    
                 }
                 
                 System.out.println("AFTER");
         System.out.println("Num: " + nOut.getNum().size());
         System.out.println("AbsNum: " + nOut.getAbstractNum().size());

         output.getMainDocumentPart().getNumberingDefinitionsPart().setContents(nOut);
                 }     
       
    private <T> void walk(Node<T> element, Set<BigInteger> set) {
        if (element==null) {return;}
        if (element.getData()!=null) {
                StyleTree.AugmentedStyle augmentedStyle = (StyleTree.AugmentedStyle)element.getData();
                Style s = augmentedStyle.getStyle();
                if (s.getPPr()!=null
                                && s.getPPr().getNumPr()!=null) {
                       
                        NumPr numPr = s.getPPr().getNumPr();
                        set.add(numPr.getNumId().getVal());
                        log.debug("Added NumId " + numPr.getNumId().getVal());                         
                } else {
                        log.debug("No numbering in style " + s.getName() + ", id " + s.getStyleId());                                                  
                }
        }
        for (Node<T> data : element.getChildren()) {
            walk(data, set);
        }
    }
   
        /**
         * Traverse the document, and return a map of all numbering which are used
         * directly in the document.  
         * @return
         */

        public Set<BigInteger> getNumberingInUse(MainDocumentPart mdp, Set<BigInteger> numId){

               
                org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document)mdp.getJaxbElement();
                Body body =  wmlDocumentEl.getBody();

                List <Object> bodyChildren = body.getContent();
               
                Set<BigInteger> numInUse = new HashSet<BigInteger>();
                NumberingFinder finder = new NumberingFinder(numId);

                new TraversalUtil(bodyChildren, finder);
               
                // Styles in headers, footers?
                RelationshipsPart rp = mdp.getRelationshipsPart();
                if (rp!=null) {
                        for ( Relationship r : rp.getRelationships().getRelationship() ) {
                                Part part = rp.getPart(r);
                                if ( part instanceof FooterPart ) {
                                       
                                        Ftr ftr = ((FooterPart)part).getJaxbElement();
                                        finder.walkJAXBElements(ftr);
                                       
                                } else if (part instanceof HeaderPart) {
                                       
                                        Hdr hdr = ((HeaderPart)part).getJaxbElement();
                                        finder.walkJAXBElements(hdr);
                                }
                        }
                }
               
                // Styles in endnotes, footnotes?
                if (mdp.getEndNotesPart()!=null) {
                        log.debug("Looking at endnotes");
                        CTEndnotes endnotes= mdp.getEndNotesPart().getJaxbElement();
                        finder.walkJAXBElements(endnotes);
                }
                if (mdp.getFootnotesPart()!=null) {
                        log.debug("Looking at footnotes");
                        CTFootnotes footnotes= mdp.getFootnotesPart().getJaxbElement();
                        finder.walkJAXBElements(footnotes);
                }
               
                // Comments
                if (mdp.getCommentsPart()!=null) {
                        log.debug("Looking at comments");                      
                        Comments comments = mdp.getCommentsPart().getJaxbElement();
                        finder.walkJAXBElements(comments);
                }
               
                return numInUse;
        }
   
       
    /**
     * Traverse looking for numbering
     *
     */

    private static class NumberingFinder extends CallbackImpl {
               
        Set<BigInteger> numInUse; // by ID
       
        NumberingFinder(Set<BigInteger> numInUse) {
               
                this.numInUse = numInUse;
        }
               
                @Override
                public List<Object> apply(Object o) {
                       
                        if (o instanceof org.docx4j.wml.P) {
                                PPr pPr = ((P)o).getPPr();
                        if (pPr!=null
                                        && pPr.getNumPr()!=null) {
                               
                                NumPr numPr = pPr.getNumPr();
                                numInUse.add(numPr.getNumId().getVal());
                                log.debug("Added NumId " + numPr.getNumId().getVal());                         
                        } else {
                                log.debug("No numbering in w:p " );                                            
                        }
                        }
                               
                        return null;
                }
       
        @Override
                public boolean shouldTraverse(Object o) {
               
                if (o instanceof org.docx4j.wml.Br
                                || o instanceof org.docx4j.wml.R.Tab
                                || o instanceof org.docx4j.wml.R.LastRenderedPageBreak) {
                        return false;
                }
                        return true;
                }
       
        }      
       
       
}
 
Parsed in 0.034 seconds, using GeSHi 1.0.8.4

Re: Numbering Part missing values

PostPosted: Thu May 11, 2023 2:37 am
by petefc
Thanks for you response:
for the attached we received this error: 0 level missing for abstractListDefinition 38

I had to zip the numbering part to get around the filesize limations