File: //home/arjun/projects/env/lib/python3.10/site-packages/docx/oxml/text/parfmt.py
"""Custom element classes related to paragraph properties (CT_PPr)."""
from __future__ import annotations
from typing import TYPE_CHECKING, Callable
from docx.enum.text import (
WD_ALIGN_PARAGRAPH,
WD_LINE_SPACING,
WD_TAB_ALIGNMENT,
WD_TAB_LEADER,
)
from docx.oxml.simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure
from docx.oxml.xmlchemy import (
BaseOxmlElement,
OneOrMore,
OptionalAttribute,
RequiredAttribute,
ZeroOrOne,
)
from docx.shared import Length
if TYPE_CHECKING:
from docx.oxml.section import CT_SectPr
from docx.oxml.shared import CT_String
class CT_Ind(BaseOxmlElement):
"""``<w:ind>`` element, specifying paragraph indentation."""
left = OptionalAttribute("w:left", ST_SignedTwipsMeasure)
right = OptionalAttribute("w:right", ST_SignedTwipsMeasure)
firstLine = OptionalAttribute("w:firstLine", ST_TwipsMeasure)
hanging = OptionalAttribute("w:hanging", ST_TwipsMeasure)
class CT_Jc(BaseOxmlElement):
"""``<w:jc>`` element, specifying paragraph justification."""
val = RequiredAttribute("w:val", WD_ALIGN_PARAGRAPH)
class CT_PPr(BaseOxmlElement):
"""``<w:pPr>`` element, containing the properties for a paragraph."""
get_or_add_pStyle: Callable[[], CT_String]
_insert_sectPr: Callable[[CT_SectPr], None]
_remove_pStyle: Callable[[], None]
_remove_sectPr: Callable[[], None]
_tag_seq = (
"w:pStyle",
"w:keepNext",
"w:keepLines",
"w:pageBreakBefore",
"w:framePr",
"w:widowControl",
"w:numPr",
"w:suppressLineNumbers",
"w:pBdr",
"w:shd",
"w:tabs",
"w:suppressAutoHyphens",
"w:kinsoku",
"w:wordWrap",
"w:overflowPunct",
"w:topLinePunct",
"w:autoSpaceDE",
"w:autoSpaceDN",
"w:bidi",
"w:adjustRightInd",
"w:snapToGrid",
"w:spacing",
"w:ind",
"w:contextualSpacing",
"w:mirrorIndents",
"w:suppressOverlap",
"w:jc",
"w:textDirection",
"w:textAlignment",
"w:textboxTightWrap",
"w:outlineLvl",
"w:divId",
"w:cnfStyle",
"w:rPr",
"w:sectPr",
"w:pPrChange",
)
pStyle: CT_String | None = ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
"w:pStyle", successors=_tag_seq[1:]
)
keepNext = ZeroOrOne("w:keepNext", successors=_tag_seq[2:])
keepLines = ZeroOrOne("w:keepLines", successors=_tag_seq[3:])
pageBreakBefore = ZeroOrOne("w:pageBreakBefore", successors=_tag_seq[4:])
widowControl = ZeroOrOne("w:widowControl", successors=_tag_seq[6:])
numPr = ZeroOrOne("w:numPr", successors=_tag_seq[7:])
tabs = ZeroOrOne("w:tabs", successors=_tag_seq[11:])
spacing = ZeroOrOne("w:spacing", successors=_tag_seq[22:])
ind = ZeroOrOne("w:ind", successors=_tag_seq[23:])
jc = ZeroOrOne("w:jc", successors=_tag_seq[27:])
sectPr = ZeroOrOne("w:sectPr", successors=_tag_seq[35:])
del _tag_seq
@property
def first_line_indent(self):
"""A |Length| value calculated from the values of `w:ind/@w:firstLine` and
`w:ind/@w:hanging`.
Returns |None| if the `w:ind` child is not present.
"""
ind = self.ind
if ind is None:
return None
hanging = ind.hanging
if hanging is not None:
return Length(-hanging)
firstLine = ind.firstLine
if firstLine is None:
return None
return firstLine
@first_line_indent.setter
def first_line_indent(self, value):
if self.ind is None and value is None:
return
ind = self.get_or_add_ind()
ind.firstLine = ind.hanging = None
if value is None:
return
elif value < 0:
ind.hanging = -value
else:
ind.firstLine = value
@property
def ind_left(self):
"""The value of `w:ind/@w:left` or |None| if not present."""
ind = self.ind
if ind is None:
return None
return ind.left
@ind_left.setter
def ind_left(self, value):
if value is None and self.ind is None:
return
ind = self.get_or_add_ind()
ind.left = value
@property
def ind_right(self):
"""The value of `w:ind/@w:right` or |None| if not present."""
ind = self.ind
if ind is None:
return None
return ind.right
@ind_right.setter
def ind_right(self, value):
if value is None and self.ind is None:
return
ind = self.get_or_add_ind()
ind.right = value
@property
def jc_val(self) -> WD_ALIGN_PARAGRAPH | None:
"""Value of the `<w:jc>` child element or |None| if not present."""
return self.jc.val if self.jc is not None else None
@jc_val.setter
def jc_val(self, value):
if value is None:
self._remove_jc()
return
self.get_or_add_jc().val = value
@property
def keepLines_val(self):
"""The value of `keepLines/@val` or |None| if not present."""
keepLines = self.keepLines
if keepLines is None:
return None
return keepLines.val
@keepLines_val.setter
def keepLines_val(self, value):
if value is None:
self._remove_keepLines()
else:
self.get_or_add_keepLines().val = value
@property
def keepNext_val(self):
"""The value of `keepNext/@val` or |None| if not present."""
keepNext = self.keepNext
if keepNext is None:
return None
return keepNext.val
@keepNext_val.setter
def keepNext_val(self, value):
if value is None:
self._remove_keepNext()
else:
self.get_or_add_keepNext().val = value
@property
def pageBreakBefore_val(self):
"""The value of `pageBreakBefore/@val` or |None| if not present."""
pageBreakBefore = self.pageBreakBefore
if pageBreakBefore is None:
return None
return pageBreakBefore.val
@pageBreakBefore_val.setter
def pageBreakBefore_val(self, value):
if value is None:
self._remove_pageBreakBefore()
else:
self.get_or_add_pageBreakBefore().val = value
@property
def spacing_after(self):
"""The value of `w:spacing/@w:after` or |None| if not present."""
spacing = self.spacing
if spacing is None:
return None
return spacing.after
@spacing_after.setter
def spacing_after(self, value):
if value is None and self.spacing is None:
return
self.get_or_add_spacing().after = value
@property
def spacing_before(self):
"""The value of `w:spacing/@w:before` or |None| if not present."""
spacing = self.spacing
if spacing is None:
return None
return spacing.before
@spacing_before.setter
def spacing_before(self, value):
if value is None and self.spacing is None:
return
self.get_or_add_spacing().before = value
@property
def spacing_line(self):
"""The value of `w:spacing/@w:line` or |None| if not present."""
spacing = self.spacing
if spacing is None:
return None
return spacing.line
@spacing_line.setter
def spacing_line(self, value):
if value is None and self.spacing is None:
return
self.get_or_add_spacing().line = value
@property
def spacing_lineRule(self):
"""The value of `w:spacing/@w:lineRule` as a member of the :ref:`WdLineSpacing`
enumeration.
Only the `MULTIPLE`, `EXACTLY`, and `AT_LEAST` members are used. It is the
responsibility of the client to calculate the use of `SINGLE`, `DOUBLE`, and
`MULTIPLE` based on the value of `w:spacing/@w:line` if that behavior is
desired.
"""
spacing = self.spacing
if spacing is None:
return None
lineRule = spacing.lineRule
if lineRule is None and spacing.line is not None:
return WD_LINE_SPACING.MULTIPLE
return lineRule
@spacing_lineRule.setter
def spacing_lineRule(self, value):
if value is None and self.spacing is None:
return
self.get_or_add_spacing().lineRule = value
@property
def style(self) -> str | None:
"""String contained in `./w:pStyle/@val`, or None if child is not present."""
pStyle = self.pStyle
if pStyle is None:
return None
return pStyle.val
@style.setter
def style(self, style: str | None):
"""Set `./w:pStyle/@val` `style`, adding a new element if necessary.
If `style` is |None|, remove `./w:pStyle` when present.
"""
if style is None:
self._remove_pStyle()
return
pStyle = self.get_or_add_pStyle()
pStyle.val = style
@property
def widowControl_val(self):
"""The value of `widowControl/@val` or |None| if not present."""
widowControl = self.widowControl
if widowControl is None:
return None
return widowControl.val
@widowControl_val.setter
def widowControl_val(self, value):
if value is None:
self._remove_widowControl()
else:
self.get_or_add_widowControl().val = value
class CT_Spacing(BaseOxmlElement):
"""``<w:spacing>`` element, specifying paragraph spacing attributes such as space
before and line spacing."""
after = OptionalAttribute("w:after", ST_TwipsMeasure)
before = OptionalAttribute("w:before", ST_TwipsMeasure)
line = OptionalAttribute("w:line", ST_SignedTwipsMeasure)
lineRule = OptionalAttribute("w:lineRule", WD_LINE_SPACING)
class CT_TabStop(BaseOxmlElement):
"""`<w:tab>` element, representing an individual tab stop.
Overloaded to use for a tab-character in a run, which also uses the w:tab tag but
only needs a __str__ method.
"""
val = RequiredAttribute("w:val", WD_TAB_ALIGNMENT)
leader = OptionalAttribute("w:leader", WD_TAB_LEADER, default=WD_TAB_LEADER.SPACES)
pos = RequiredAttribute("w:pos", ST_SignedTwipsMeasure)
def __str__(self) -> str:
"""Text equivalent of a `w:tab` element appearing in a run.
Allows text of run inner-content to be accessed consistently across all text
inner-content.
"""
return "\t"
class CT_TabStops(BaseOxmlElement):
"""``<w:tabs>`` element, container for a sorted sequence of tab stops."""
tab = OneOrMore("w:tab", successors=())
def insert_tab_in_order(self, pos, align, leader):
"""Insert a newly created `w:tab` child element in `pos` order."""
new_tab = self._new_tab()
new_tab.pos, new_tab.val, new_tab.leader = pos, align, leader
for tab in self.tab_lst:
if new_tab.pos < tab.pos:
tab.addprevious(new_tab)
return new_tab
self.append(new_tab)
return new_tab