-
-
Notifications
You must be signed in to change notification settings - Fork 485
Numbering
DOCX is quite flexible in its bullets and numbering system, allowing the user great freedom in how bullets and numbers are to be styled and displayed. E.g., numbers can be shown using Arabic numerals, roman numerals, or even ordinal words ("one", "two", "three", ...). The format also supports re-using bullets/numbering styles throughout the document, so that different lists using the same style need not redefine them.
Because of this flexibility, bullets and numbering in DOCX involves a couple of moving pieces:
- Document-level bullets/numbering definitions (abstract)
- Document-level bullets/numbering definitions (concrete)
- Paragraph-level bullets/numbering selection
Every document contains a set of abstract bullets/numbering definitions which define the formatting and layout of paragraphs using those bullets/numbering. An abstract numbering system defines how bullets/numbers are to be shown for lists, including any sublists that may be used. Thus each abstract definition includes a series of levels which form a sequence starting at 0 indicating the top-level list look and increasing from there to descibe the sublists, then sub-sublists, etc. Each level includes the following properties:
- level: This its 0-based index in the defintion stack
-
numberFormat: This indicates how the bullet or number should be
generated. Options include
bullet
(meaning don't count),decimal
(arabic numerals),upperRoman
,lowerRoman
,hex
, and many more. -
levelText: This is a format string using the output of the
numberFormat
function and generating a string to insert before every item in the list. You may use%1
,%2
, ... to reference the numbers from each numbering level before this one. Thus a level text of%d)
with a number format oflowerLetter
would result in the sequence "a)", "b)", ... - and a few others, which you can see in the OXML spec section 17.9.6
Concrete definitions are sort of like concrete subclasses of the abstract defintions. They indicate their parent and are allowed to override certain level definitions. Thus two lists that differ only in how sub-sub-lists are to be displayed can share the same abstract numbering definition and have slightly different concrete definitions.
In order to use a bullets/numbering definition (which must be concrete), paragraphs need to select it, similar to applying a CSS class to an element, using both the concrete numbering definition ID and the level number that the paragraph should be at. Additionally, MS Word and LibreOffice typically apply a "ListParagraph" style to paragraphs that are being numbered.
docx
includes a pre-defined bullet style which you can add to your
paragraphs using para.bullets()
. If you require different bullet
styles or numbering of any kind, you'll have to use the
docx.Numbering
class.
First you need to create a new numbering container class and use it to create your abstract numbering style, define your levels, and creat your concreate numbering style:
const numbering = new Numbering();
const abstractNum = numbering.createAbstractNumbering();
abstractNum.createLevel(0, "upperRoman", "%1", "start")
.addParagraphProperty(new Indent(720, 260));
abstractNum.createLevel(1, "decimal", "%2.", "start")
.addParagraphProperty(new Indent(1440, 980));
abstractNum.createLevel(2, "lowerLetter", "%3)", "start")
.addParagraphProperty(new Indent(2160, 1700));
const concrete = numbering.createConcreteNumbering(numberedAbstract);
You can then apply your concrete style to paragraphs using their
#setNumbering
method:
topLevelP.setNumbering(concrete, 0);
subP.setNumbering(concrete, 1);
subSubP.setNumbering(concrete, 2);
Finally, you need to let your exporter know about your numbering styles when you're ready to render the document:
const packer = new Packer(doc, undefined, undefined, numbering);
packer.pack(myOutput);