# How to Create and Format Lists in Word Documents Using Python

A runbook needs numbered steps. A requirements checklist reads better with bullets. A specification document usually wants a hierarchy such as "1. Overview / 1.1 Background / 1.1.1 Scope". In Word this is a click on the numbering button, but when the document is generated by a script, and the number of items changes from run to run, clicking is not an option.

A Word list is not just a text prefix. It is a structure made of a list style plus per-paragraph list formatting. Once those two layers are clear, Python can produce lists at any depth and starting from any number. This article shows how to create numbered lists, bulleted lists, multi-level numbering, and restarted numbering in Spire.Doc for Python.

## Why Build Lists in a Script

- **Stable structure**: numbering is driven by the style, so inserting or deleting items renumbers automatically.
- **Consistent levels**: depth is set with `ListLevelNumber`, so indentation no longer drifts between items.
- **Repeatable**: the same list style applies to any number of generated documents, with the item text coming from data.

## Setting Up the Environment

Install the Spire.Doc library:

```bash
pip install Spire.Doc
```

Then import the required modules in your script:

```python
from spire.doc import *
from spire.doc.common import *
```

## The Two Layers of List Formatting

Spire.Doc separates lists into two parts:

- **The list style (**`ListStyle`**)**: defines the appearance of each level — number format, prefix, starting value, and bullet character. Create one with `document.Styles.Add(ListType.Numbered, "name")`.
- **The paragraph's list format (**`ListFormat`**)**: attaches the style to a paragraph and says which level that paragraph belongs to, through `paragraph.ListFormat.ApplyStyle("name")` and `ListLevelNumber`.

The style is defined once; each paragraph then joins that numbering sequence with a single `ApplyStyle()` call.

## Using the Built-in Numbered Style

For a plain consecutive list, `ApplyNumberedStyle()` does the job in one call:

```python
from spire.doc import *
from spire.doc.common import *

document = Document()
section = document.AddSection()

items = ["Set up the environment", "Install the library", "Run the first script"]
first = True

for item in items:
    paragraph = section.AddParagraph()
    paragraph.AppendText(item)
    if first:
        paragraph.ListFormat.ApplyNumberedStyle()
        first = False
    else:
        paragraph.ListFormat.ContinueListNumbering()

document.SaveToFile("SimpleNumberedList.docx", FileFormat.Docx2013)
document.Close()
```

Two details matter here. The first paragraph calls `ApplyNumberedStyle()` to start the list; every later paragraph calls `ContinueListNumbering()` to say "keep counting from the previous item". Calling `ApplyNumberedStyle()` on every paragraph instead produces several separate lists, each restarting at 1.

## Building a Custom Numbered Style

When the number format has to be controlled — "1." or "1.1." — create a dedicated list style. Each level is configured in `ListRef.Levels`:

```python
from spire.doc import *
from spire.doc.common import *

document = Document()
section = document.AddSection()

# Create a numbered list style
numberList = document.Styles.Add(ListType.Numbered, "numberList")

levels = numberList.ListRef.Levels
# %1 through %9 are placeholders for the counters of each level
levels[0].NumberPrefix = "%1."
levels[0].PatternType = ListPatternType.Arabic
levels[1].NumberPrefix = "%1.%2."
levels[1].PatternType = ListPatternType.Arabic
levels[2].NumberPrefix = "%1.%2.%3."
levels[2].PatternType = ListPatternType.Arabic

# Add one paragraph per level
for text, level in [("Overview", 0), ("Background", 1), ("Scope", 2)]:
    paragraph = section.AddParagraph()
    paragraph.AppendText(text)
    paragraph.ListFormat.ApplyStyle(numberList.Name)
    paragraph.ListFormat.ListLevelNumber = level

document.SaveToFile("CustomNumberedList.docx", FileFormat.Docx2013)
document.Close()
```

In `NumberPrefix`, `%1`, `%2`, and `%3` stand for the counters of levels 1, 2, and 3, so a second level written as `%1.%2.` and a third written as `%1.%2.%3.` produce "1.1." and "1.1.1.". `PatternType = ListPatternType.Arabic` selects Western digits; the enum offers other numeral styles as well.

`ListLevelNumber` is **zero-based**: 0 is the first level, 1 the second, and so on. It is easy to confuse with the index into `Levels`, but the two are consistent — both start at 0.

## Bulleted Lists

Bulleted lists use the same machinery, with `ListType.Bulleted` in place of `ListType.Numbered`:

```python
from spire.doc import *
from spire.doc.common import *

document = Document()
section = document.AddSection()

# Create a bulleted list style
bulletList = document.Styles.Add(ListType.Bulleted, "bulletList")

items = ["Reusable template", "Batch output", "Consistent formatting"]
for item in items:
    paragraph = section.AddParagraph()
    paragraph.AppendText(item)
    paragraph.ListFormat.ApplyStyle(bulletList.Name)

document.SaveToFile("BulletedList.docx", FileFormat.Docx2013)
document.Close()
```

To change the symbol, set `BulletCharacter` on the level. A Word bullet is really a character, so any character that maps to a glyph in the right font will do:

```python
# Use a character from the Wingdings font as the bullet
bulletList = document.Styles.Add(ListType.Bulleted, "squareBullet")
levels = bulletList.ListRef.Levels
levels[0].BulletCharacter = "\u006e"
levels[0].CharacterFormat.FontName = "Wingdings"
```

In Wingdings, `\u006e` renders as a solid square; `\u0075`, `\u00b2`, or `\u00d8` yield other symbols. The essential part is that `CharacterFormat.FontName` must match the symbol font the character came from, or the bullet shows up as a blank box or a question mark.

## Starting a List From a Given Number

Sometimes a second block of items must continue a numbering scheme, or deliberately begin at a specific number. Rather than building two styles, set the starting value:

```python
from spire.doc import *
from spire.doc.common import *

document = Document()
section = document.AddSection()

numberList = document.Styles.Add(ListType.Numbered, "Numbered1")
for text in ["List Item 1", "List Item 2"]:
    paragraph = section.AddParagraph()
    paragraph.AppendText(text)
    paragraph.ListFormat.ApplyStyle(numberList.Name)

# The second list starts at 10
numberList2 = document.Styles.Add(ListType.Numbered, "Numbered2")
numberList2.ListRef.Levels[0].StartAt = 10

for text in ["List Item 3", "List Item 4"]:
    paragraph = section.AddParagraph()
    paragraph.AppendText(text)
    paragraph.ListFormat.ApplyStyle(numberList2.Name)

document.SaveToFile("RestartList.docx", FileFormat.Docx2013)
document.Close()
```

`StartAt` applies to the level it is set on; setting it on the first level restarts the whole sequence from that value. If the two blocks should belong to one continuous sequence with a gap in between, `ContinueListNumbering()` is the better fit — `StartAt` is for a deliberate restart at a chosen number.

## Practical Tips

- **Create the style once**: `Styles.Add()` is called a single time; every paragraph then reuses it through `ApplyStyle()`.
- **Levels are zero-based**: `ListLevelNumber` and the `Levels` index agree, and an off-by-one shifts both the prefix and the indentation.
- **Do not mix** `ApplyNumberedStyle()` **and** `ApplyStyle()`: the former brings its own built-in numbering while the latter references a custom style, and mixing them in one block breaks the sequence.
- **Match the symbol font**: a custom `BulletCharacter` needs its `FontName` set, and that font has to be available where the document is opened.
- **Release resources**: call `document.Close()` after saving, and in batch jobs create, save, and close one document at a time.

## Conclusion

This article covered several ways to build lists in Word documents with Python: quick consecutive numbering with `ApplyNumberedStyle()`, multi-level number formats defined through `Styles.Add(ListType.Numbered, ...)` and `Levels[i].NumberPrefix`, custom bullets with `ListType.Bulleted` and `BulletCharacter`, and control over continuation and restart through `ContinueListNumbering()` and `StartAt`. Because numbering is driven by the style, the sequence adjusts itself whenever the number of items changes, which makes these operations a good fit for template-based document generation.
