Issue with setRestartAfterLevel Not Restarting List Numbers in Aspose.Words?

Hello Aspose Support Team,

I am currently working with Aspose.Words for Java, and I’m facing an issue when using the setRestartAfterLevel() method on a list level. The problem is that the numbering of the list is continuing instead of restarting as expected.

Here is a summary of my use case:

  • I have a multi-level list, and I want the numbering to restart at a specific level.
  • I am using the setRestartAfterLevel() method to specify that numbering should restart after a certain level.
  • However, the numbers continue from the previous level, even though I have correctly applied the setRestartAfterLevel() method.
private void setRestartAfterLevelProperty(
      Lists.Level stylingInstructionsLevel, ListLevel documentListLevel) {
    Optional.ofNullable(stylingInstructionsLevel.getLevelRestartAt())
        .ifPresent(documentListLevel::setRestartAfterLevel);
}

I am expecting the list to restart numbering at the specified level, but instead, the numbering continues.
Things I have already tried:

  • Ensuring that the list levels are correctly defined.
  • Double-checking that the stylingInstructionsLevel.getLevelRestartAt() is returning a valid level.
  • Calling setRestartAfterLevel() after setting other list properties.

Could you please help me understand what might be causing this issue? Is there anything specific I need to do to make the restart behavior work correctly?
THIS WAS THE FULL CODE SNIPPET:

package com.trgr.ras.cd.firmstylewordgenerator.service.processors;

import static org.apache.commons.collections4.CollectionUtils.isEmpty;

import com.aspose.words.*;
import com.aspose.words.Font;
import com.aspose.words.List;
import com.trgr.ras.cd.firmstylewordgenerator.entity.Lists;
import com.trgr.ras.cd.firmstylewordgenerator.entity.StylingInstructions;
import com.trgr.ras.cd.firmstylewordgenerator.service.AsposeDocumentHelper;
import java.awt.*;
import java.util.Optional;
import lombok.CustomLog;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

@Service
@CustomLog
public class ListProcessor extends StyleProcessor {

  @Override
  public void process(Document document, StylingInstructions stylingInstructions) {
    if (stylingInstructions == null || isEmpty(stylingInstructions.getLists())) {
      return;
    }

    try {
      stylingInstructions
          .getLists()
          .forEach(list -> applyStylingInstructionsForLevels(document, list));
    } catch (Exception e) {
      log.error("Error processing lists styling info", e);
    }
  }

  private void applyStylingInstructionsForLevels(Document document, Lists stylingInstructionsList) {
    var newList = document.getLists().add(ListTemplate.NUMBER_DEFAULT);
    for (int i = 0; i < stylingInstructionsList.getLevels().size(); i++) {
      Lists.Level stylingInstructionsLevel = stylingInstructionsList.getLevels().get(i);

      var documentListLevel = newList.getListLevels().get(i);
      setStartAtProperty(stylingInstructionsLevel, documentListLevel);
      setRestartAfterLevelProperty(stylingInstructionsLevel, documentListLevel);
      setNumberStyleProperty(stylingInstructionsLevel, documentListLevel);
      setNumberFormatProperty(stylingInstructionsLevel, documentListLevel);
      setAlignmentProperty(stylingInstructionsLevel, documentListLevel);
      setTrailingCharacterProperty(stylingInstructionsLevel, documentListLevel);
      setParagraphProperty(stylingInstructionsLevel, documentListLevel);
      setFontProperty(stylingInstructionsLevel, documentListLevel, document);
      setLinkedStyle(stylingInstructionsLevel, newList, documentListLevel, i, document);
    }
  }

  private void setLinkedStyle(
      Lists.Level stylingInstructionsLevel,
      List newList,
      ListLevel documentListLevel,
      int listLevelNumber,
      Document document) {
    Optional.ofNullable(stylingInstructionsLevel.getStyle())
        .filter(StringUtils::isNotBlank)
        .ifPresent(
            (styleName) -> {
              var linkedStyle = document.getStyles().get(styleName);
              linkedStyle.getListFormat().removeNumbers();
              documentListLevel.setLinkedStyle(linkedStyle);

              // When a style is added to a list in ASPOSE, the list levels indent level is added to
              // the style left indent. this isn't consistent with our styling instructions, so we
              // need to save and restore the original left indent, provided there is one set (>0.0)
              var originalLeftIndent = linkedStyle.getParagraphFormat().getLeftIndent();
              linkedStyle.getListFormat().setList(newList);

              if (originalLeftIndent != 0.0) {
                linkedStyle.getParagraphFormat().setLeftIndent(originalLeftIndent);
              }

              linkedStyle.getListFormat().setListLevelNumber(listLevelNumber);
            });
  }

  private void setTrailingCharacterProperty(
      Lists.Level stylingInstructionsLevel, ListLevel documentListLevel) {
    Optional.ofNullable(stylingInstructionsLevel.getTrailingCharacter())
        .filter(StringUtils::isNotBlank)
        .ifPresentOrElse(
            character ->
                documentListLevel.setTrailingCharacter(ListTrailingCharacter.fromName(character)),
            () -> documentListLevel.setTrailingCharacter(ListTrailingCharacter.TAB));
  }

  private void setAlignmentProperty(
      Lists.Level stylingInstructionsLevel, ListLevel documentListLevel) {
    Optional.ofNullable(stylingInstructionsLevel.getAlignment())
        .filter(StringUtils::isNotBlank)
        .ifPresent(
            alignment -> documentListLevel.setAlignment(ListLevelAlignment.fromName(alignment)));
  }

  private void setNumberFormatProperty(
      Lists.Level stylingInstructionsLevel, ListLevel documentListLevel) {
    Optional.ofNullable(stylingInstructionsLevel.getNumberFormat())
        .ifPresent(
            (formatString) -> {
              documentListLevel.setNumberFormat(processNumberFormat(formatString));
            });
  }

  private String processNumberFormat(String input) {
    var percentLoc = input.indexOf("%");
    while (percentLoc >= 0) {
      var listLevel = Integer.parseInt(input.substring(percentLoc + 1, percentLoc + 2));
      var placeholderChar = String.valueOf((char) (listLevel - 1));
      input = input.substring(0, percentLoc) + placeholderChar + input.substring(percentLoc + 2);
      percentLoc = input.indexOf("%");
    }

    return input;
  }

  private void setNumberStyleProperty(
      Lists.Level stylingInstructionsLevel, ListLevel documentListLevel) {
    Optional.ofNullable(stylingInstructionsLevel.getNumberStyle())
        .filter(StringUtils::isNotBlank)
        .ifPresent(
            style -> {
              documentListLevel.setNumberStyle(NumberStyle.fromName(style));
            });
  }

  private void setRestartAfterLevelProperty(
      Lists.Level stylingInstructionsLevel, ListLevel documentListLevel) {
    Optional.ofNullable(stylingInstructionsLevel.getLevelRestartAt())
        .ifPresent(documentListLevel::setRestartAfterLevel);
  }

  private void setStartAtProperty(
      Lists.Level stylingInstructionsLevel, ListLevel documentListLevel) {
    Optional.ofNullable(stylingInstructionsLevel.getStartAt())
        .ifPresent(documentListLevel::setStartAt);
  }

  private void setParagraphProperty(Lists.Level level, ListLevel listLevel) {
    Optional.ofNullable(level.getParagraph())
        .ifPresent(
            paragraph -> {
              Optional.ofNullable(paragraph.getAlignment())
                  .ifPresent(
                      alignment ->
                          listLevel.setAlignment(ListLevelAlignment.fromName(alignment.name())));

              Optional.ofNullable(paragraph.getTabPosition())
                  .ifPresentOrElse(
                      tabPosition -> listLevel.setTabPosition(ConvertUtil.inchToPoint(tabPosition)),
                      () -> listLevel.setTabPosition(0));

              Optional.ofNullable(paragraph.getLeftIndent())
                  .ifPresent(
                      leftIndent -> listLevel.setTextPosition(ConvertUtil.inchToPoint(leftIndent)));

              // compute number position
              double numberPositionInches = 0;
              if (paragraph.getLeftIndent() != null) {
                numberPositionInches += paragraph.getLeftIndent();
              }

              if (paragraph.getFirstLineIndent() != null) {
                numberPositionInches += paragraph.getFirstLineIndent();
              } else if (paragraph.getHangingIndent() != null) {
                numberPositionInches += paragraph.getHangingIndent();
              }

              listLevel.setNumberPosition(ConvertUtil.inchToPoint(numberPositionInches));
            });
  }

  private void setFontProperty(Lists.Level level, ListLevel listLevel, Document document) {
    if (level.getFont() == null) {
      Optional.ofNullable(level.getStyle())
          .map(styleName -> document.getStyles().get(styleName))
          .map(style -> style.getFont())
          .ifPresent(
              srcFont -> {
                Font destinationFont = listLevel.getFont();
                AsposeDocumentHelper.copyFontData(srcFont, destinationFont);
              });
    } else {
      Optional.ofNullable(level.getFont()).ifPresent(font -> updateFont(listLevel.getFont(), font));
    }
  }
}

@AAddd

It seems you are experiencing an issue with the setRestartAfterLevel() method in Aspose.Words for Java, where the list numbering does not restart as expected. Here are some steps and considerations that might help you resolve this issue:

  1. Ensure Correct Level Configuration: Make sure that the level you are trying to restart after is correctly defined and that the stylingInstructionsLevel.getLevelRestartAt() method returns a valid level. If this value is not set correctly, the numbering will not restart as intended.

  2. Order of Method Calls: The order in which you set properties on the list levels can affect the outcome. Ensure that you are calling setRestartAfterLevel() after all other properties have been set for that list level. This can sometimes help in ensuring that the restart behavior is applied correctly.

  3. Check for List Level Properties: Verify that the properties of the list levels are set correctly. For example, ensure that the setStartAt() method is also configured properly, as it can influence how the numbering behaves.

  4. Example Code: Here’s a simplified example of how to set up a multi-level list with the restart behavior:

    Document doc = new Document();
    List list = doc.getLists().add(ListTemplate.NUMBER_DEFAULT);
    
    // Configure the first level
    ListLevel level1 = list.getListLevels().get(0);
    level1.setRestartAfterLevel(-1); // This means it will not restart
    level1.setStartAt(1);
    
    // Configure the second level
    ListLevel level2 = list.getListLevels().get(1);
    level2.setRestartAfterLevel(0); // This means it will restart after level 1
    level2.setStartAt(1);
    
    DocumentBuilder builder = new DocumentBuilder(doc);
    builder.getListFormat().setList(list);
    builder.writeln("Item 1");
    builder.getListFormat().listIndent();
    builder.writeln("Subitem 1.1");
    builder.getListFormat().listOutdent();
    builder.writeln("Item 2");
    builder.getListFormat().removeNumbers();
    
    doc.save("output.docx");
    
  5. Review Documentation: If the issue persists, consider reviewing the official documentation for ListLevel and setRestartAfterLevel() to ensure that you are using the methods correctly. The documentation provides detailed information on how to configure list levels and their properties.

If you continue to face issues, please provide additional details about the specific behavior you are observing, and I can assist you further.

@AAddd I suppose you misuse RestartAfterLevel property that specifies the list level that must appear before the specified list level restarts numbering. It does not restart numbering at the certain item.

If you need to restart numbering of list, this property will not help. When you do restart numbering in MS Word, MS word simply creates a new list. So in this case you should also create a new list. Please see our documentation to learn more about lists:
https://docs.aspose.com/words/java/working-with-lists/

For example the following code demonstrates how to restart numbering:

Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);

// Start list.
builder.getListFormat().applyNumberDefault();
builder.writeln("Test");
builder.writeln("Test");
builder.writeln("Test");
builder.writeln("Test");
// Restart numbering.
com.aspose.words.List list = doc.getLists().addCopy(builder.getListFormat().getList());
builder.getListFormat().setList(list);
builder.writeln("Test");
builder.writeln("Test");
builder.writeln("Test");
builder.writeln("Test");
builder.getListFormat().removeNumbers();
// save output.
doc.save("C:\\Temp\\out.docx");

Alternatively you can insert section break between list parts and use isRestartAtEachSection property.