python docx中的项目符号列表

2024-05-17 02:55:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我正试图让它在python-docx中工作:

enter image description here

我可以使用这个项目符号列表:

from docx import Document
doc = Document()
p = doc.add_paragraph()
p.style = 'List Bullet'

r = p.add_run()
r.add_text("Item 1")
# Something's gotta come here to get the Sub-Item 1
r = p.add_run()
r.add_text("Item 2")    
# Something's gotta come here to get the Sub-Item 2

我想,在中间添加另一个段落不会有帮助,因为这基本上意味着我正在制作另一个List Bullet,格式与其父格式相同,而不是我想要的子格式。另外,在同一段落中添加另一个run也没有帮助(我试过了,把整个事情搞砸了)。有办法吗?


Tags: toruntextadddochere格式item
1条回答
网友
1楼 · 发布于 2024-05-17 02:55:18

有一种方法可以做到这一点,但这需要你做一些额外的工作。python docx中目前没有用于此操作的“本机”接口。每个项目符号必须是单独的段落。运行仅适用于文本字符。

其思想是列表项目符号或编号由具体的项目符号或编号样式控制,该样式指的是抽象样式。抽象样式决定了受影响段落的样式,而具体的编号决定了抽象序列中的数字/项目符号。这意味着您可以在项目符号段落之间插入没有项目符号和编号的段落。同时,通过创建新的具体样式,可以在任意点重新启动编号/项目符号序列。

所有这些信息都在Issue #25中散列出来(详细但不成功)。我现在没有时间和资源来解决这个问题,但我确实在讨论线程的comment中编写了一个函数。此函数将根据所需的缩进级别和段落样式查找抽象样式。然后,它将基于该抽象样式创建或检索具体样式,并将其分配给段落对象:

def list_number(doc, par, prev=None, level=None, num=True):
    """
    Makes a paragraph into a list item with a specific level and
    optional restart.

    An attempt will be made to retreive an abstract numbering style that
    corresponds to the style of the paragraph. If that is not possible,
    the default numbering or bullet style will be used based on the
    ``num`` parameter.

    Parameters
    ----------
    doc : docx.document.Document
        The document to add the list into.
    par : docx.paragraph.Paragraph
        The paragraph to turn into a list item.
    prev : docx.paragraph.Paragraph or None
        The previous paragraph in the list. If specified, the numbering
        and styles will be taken as a continuation of this paragraph.
        If omitted, a new numbering scheme will be started.
    level : int or None
        The level of the paragraph within the outline. If ``prev`` is
        set, defaults to the same level as in ``prev``. Otherwise,
        defaults to zero.
    num : bool
        If ``prev`` is :py:obj:`None` and the style of the paragraph
        does not correspond to an existing numbering style, this will
        determine wether or not the list will be numbered or bulleted.
        The result is not guaranteed, but is fairly safe for most Word
        templates.
    """
    xpath_options = {
        True: {'single': 'count(w:lvl)=1 and ', 'level': 0},
        False: {'single': '', 'level': level},
    }

    def style_xpath(prefer_single=True):
        """
        The style comes from the outer-scope variable ``par.style.name``.
        """
        style = par.style.style_id
        return (
            'w:abstractNum['
                '{single}w:lvl[@w:ilvl="{level}"]/w:pStyle[@w:val="{style}"]'
            ']/@w:abstractNumId'
        ).format(style=style, **xpath_options[prefer_single])

    def type_xpath(prefer_single=True):
        """
        The type is from the outer-scope variable ``num``.
        """
        type = 'decimal' if num else 'bullet'
        return (
            'w:abstractNum['
                '{single}w:lvl[@w:ilvl="{level}"]/w:numFmt[@w:val="{type}"]'
            ']/@w:abstractNumId'
        ).format(type=type, **xpath_options[prefer_single])

    def get_abstract_id():
        """
        Select as follows:

            1. Match single-level by style (get min ID)
            2. Match exact style and level (get min ID)
            3. Match single-level decimal/bullet types (get min ID)
            4. Match decimal/bullet in requested level (get min ID)
            3. 0
        """
        for fn in (style_xpath, type_xpath):
            for prefer_single in (True, False):
                xpath = fn(prefer_single)
                ids = numbering.xpath(xpath)
                if ids:
                    return min(int(x) for x in ids)
        return 0

    if (prev is None or
            prev._p.pPr is None or
            prev._p.pPr.numPr is None or
            prev._p.pPr.numPr.numId is None):
        if level is None:
            level = 0
        numbering = doc.part.numbering_part.numbering_definitions._numbering
        # Compute the abstract ID first by style, then by num
        anum = get_abstract_id()
        # Set the concrete numbering based on the abstract numbering ID
        num = numbering.add_num(anum)
        # Make sure to override the abstract continuation property
        num.add_lvlOverride(ilvl=level).add_startOverride(1)
        # Extract the newly-allocated concrete numbering ID
        num = num.numId
    else:
        if level is None:
            level = prev._p.pPr.numPr.ilvl.val
        # Get the previous concrete numbering ID
        num = prev._p.pPr.numPr.numId.val
    par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_numId().val = num
    par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_ilvl().val = level

使用默认内置文档存根中的样式,可以执行以下操作:

d = docx.Document()
p0 = d.add_paragraph('Item 1', style='List Bullet')
list_number(d, p0, level=0, num=False)
p1 = d.add_paragraph('Item A', style='List Bullet 2')
list_number(d, p1, p0, level=1)
p2 = d.add_paragraph('Item 2', style='List Bullet')
list_number(d, p2, p1, level=0)
p3 = d.add_paragraph('Item B', style='List Bullet 2')
list_number(d, p3, p2, level=1)

样式不仅会影响制表位和段落的其他显示特性,而且还会帮助查找适当的抽象编号方案。当在调用p0时隐式设置prev=None时,函数将创建一个新的具体编号方案。其余的所有段落将继承相同的方案,因为它们得到一个prev参数。对list_number的调用不必与对add_paragraph的调用交叉,只要在调用之前设置用作prev的段落的编号。

相关问题 更多 >