Skip to content

Added new add_list method to enhance lists support. #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docx/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ def add_paragraph(self, text='', style=None):
"""
return self._document_part.add_paragraph(text, style)

def add_list(self, style='ListParagraph', level=0):
"""
Return a helper that implements methods to create a list formed by
paragraphs sharing the same numId, added to the end of the document,
having paragraph style *style* and indentation level *level*.
"""
return self._document_part.add_list(style=style, level=level)

def add_picture(self, image_path_or_stream, width=None, height=None):
"""
Return a new picture shape added in its own paragraph at the end of
Expand Down Expand Up @@ -137,6 +145,16 @@ def paragraphs(self):
"""
return self._document_part.paragraphs

@property
def lists(self):
"""
A list of |ListParagraph| instances corresponding to the paragraphs
grouped in lists, in document order. Note that paragraphs within
revision marks such as ``<w:ins>`` or ``<w:del>`` do not appear in this
list.
"""
return self._document_part.lists

def save(self, path_or_stream):
"""
Save this document to *path_or_stream*, which can be either a path to
Expand Down
35 changes: 35 additions & 0 deletions docx/blkcntnr.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@

from __future__ import absolute_import, print_function

import sys
import random
from .shared import Parented
from .text import Paragraph
from .list import ListParagraph


class BlockItemContainer(Parented):
Expand All @@ -23,6 +26,15 @@ def __init__(self, element, parent):
super(BlockItemContainer, self).__init__(parent)
self._element = element

def generate_numId(self):
"""
Generate a unique numId value on this container.
"""
while True:
numId = random.randint(0, 999999)
if not len(self._element.xpath("//w:numId[@w:val='%s']" % numId)):
return numId

def add_paragraph(self, text='', style=None):
"""
Return a paragraph newly added to the end of the content in this
Expand Down Expand Up @@ -52,6 +64,19 @@ def add_table(self, rows, cols):
table.add_row()
return table

def add_list(self, style=None, level=0):
"""
Return a list paragraph newly added to the end of the content in this
container, having a paragraph style *style* and an indentation level
*level*.
"""
return ListParagraph(
self,
numId=self.generate_numId(),
style=style,
level=level,
)

@property
def paragraphs(self):
"""
Expand All @@ -60,6 +85,16 @@ def paragraphs(self):
"""
return [Paragraph(p, self) for p in self._element.p_lst]

@property
def lists(self):
"""
A list containing the paragraphs grouped in lists in this container,
in document order. Read-only.
"""
nums = [paragraph.numId for paragraph in self.paragraphs
if paragraph.numId is not None]
return [ListParagraph(self, numId) for numId in set(nums)]

@property
def tables(self):
"""
Expand Down
55 changes: 55 additions & 0 deletions docx/list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# encoding: utf-8

"""
The |ListParagraph| object and related proxy classes.
"""

from __future__ import (
absolute_import, division, print_function, unicode_literals
)

import random
from .text import Paragraph


class ListParagraph(object):
"""
Proxy object for controlling a set of ``<w:p>`` grouped together in a list.
"""
def __init__(self, parent, numId=0, style=None, level=0):
self._parent = parent
self.numId = numId
self.level = level
self.style = style

def add_item(self, text=None, style=None):
"""
Add a paragraph item to the current list, having text set to *text* and
a paragraph style *style*
"""
item = self._parent.add_paragraph(text, style=style)
item.level = self.level
item.numId = self.numId
return item

def add_list(self, style=None):
"""
Add a list indented one level below the current one, having a paragraph
style *style*. Note that the document will only be altered once the
first item has been added to the list.
"""
return ListParagraph(
self._parent,
numId=self._parent.generate_numId(),
style=style if style is not None else self.style,
level=self.level+1,
)

@property
def items(self):
"""
Sequence of |Paragraph| instances corresponding to the item elements
in this list paragraph.
"""
return [paragraph for paragraph in self._parent.paragraphs
if paragraph.numId == self.numId]
7 changes: 7 additions & 0 deletions docx/oxml/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ def style(self, style):
pStyle.val = style


class CT_NumPr(BaseOxmlElement):
"""
``<w:numPr>`` element, containing properties useful for lists.
"""
numId = ZeroOrOne('w:numId')
ilvl = ZeroOrOne('w:ilvl')

class CT_R(BaseOxmlElement):
"""
``<w:r>`` element, containing the properties and text for a run.
Expand Down
19 changes: 19 additions & 0 deletions docx/parts/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ def add_table(self, rows, cols):
"""
return self.body.add_table(rows, cols)

def add_list(self, style=None, level=0):
"""
Return a helper that provides methods to add paragraphs to the end of
the body content, grouped together as a list. The paragraphs will by
default have their paragraph style set to *style*, and their indentation
level set to *level*.
"""
return self.body.add_list(style=style, level=level)

@lazyproperty
def body(self):
"""
Expand Down Expand Up @@ -95,6 +104,16 @@ def paragraphs(self):
"""
return self.body.paragraphs

@property
def lists(self):
"""
A list of |ListParagraph| instances corresponding to lists formed by
paragraphs sharing the same numId in the document, in document order.
Note that list paragraphs within revision marks such as inserted or
deleted do not appear in this list.
"""
return self.body.lists

@lazyproperty
def sections(self):
"""
Expand Down
40 changes: 40 additions & 0 deletions docx/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,46 @@ def add_run(self, text=None, style=None):
run.style = style
return run

@property
def numId(self):
"""
Return the numId of the parent list.
"""
if self._p.pPr is None:
return None
if self._p.pPr.numPr is None:
return None
if self._p.pPr.numPr.numId is None:
return None
return self._p.pPr.numPr.numId.val

@numId.setter
def numId(self, numId):
"""
Set the numId of the parent list.
"""
self._p.get_or_add_pPr().get_or_add_numPr().get_or_add_numId().val = numId

@property
def level(self):
"""
Return the indentation level of the parent list.
"""
if self._p.pPr is None:
return None
if self._p.pPr.numPr is None:
return None
if self._p.pPr.numPr.ilvl is None:
return None
return self._p.pPr.numPr.ilvl.val

@level.setter
def level(self, lvl):
"""
Set the indentation level of the parent list.
"""
self._p.get_or_add_pPr().get_or_add_numPr().get_or_add_ilvl().val = lvl

@property
def alignment(self):
"""
Expand Down