about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/docx/section.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/docx/section.py')
-rw-r--r--.venv/lib/python3.12/site-packages/docx/section.py479
1 files changed, 479 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/docx/section.py b/.venv/lib/python3.12/site-packages/docx/section.py
new file mode 100644
index 00000000..982a1437
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/docx/section.py
@@ -0,0 +1,479 @@
+"""The |Section| object and related proxy classes."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Iterator, List, Sequence, overload
+
+from docx.blkcntnr import BlockItemContainer
+from docx.enum.section import WD_HEADER_FOOTER
+from docx.oxml.text.paragraph import CT_P
+from docx.parts.hdrftr import FooterPart, HeaderPart
+from docx.shared import lazyproperty
+from docx.table import Table
+from docx.text.paragraph import Paragraph
+
+if TYPE_CHECKING:
+    from docx.enum.section import WD_ORIENTATION, WD_SECTION_START
+    from docx.oxml.document import CT_Document
+    from docx.oxml.section import CT_SectPr
+    from docx.parts.document import DocumentPart
+    from docx.parts.story import StoryPart
+    from docx.shared import Length
+
+
+class Section:
+    """Document section, providing access to section and page setup settings.
+
+    Also provides access to headers and footers.
+    """
+
+    def __init__(self, sectPr: CT_SectPr, document_part: DocumentPart):
+        super(Section, self).__init__()
+        self._sectPr = sectPr
+        self._document_part = document_part
+
+    @property
+    def bottom_margin(self) -> Length | None:
+        """Read/write. Bottom margin for pages in this section, in EMU.
+
+        `None` when no bottom margin has been specified. Assigning |None| removes any
+        bottom-margin setting.
+        """
+        return self._sectPr.bottom_margin
+
+    @bottom_margin.setter
+    def bottom_margin(self, value: int | Length | None):
+        self._sectPr.bottom_margin = value
+
+    @property
+    def different_first_page_header_footer(self) -> bool:
+        """True if this section displays a distinct first-page header and footer.
+
+        Read/write. The definition of the first-page header and footer are accessed
+        using :attr:`.first_page_header` and :attr:`.first_page_footer` respectively.
+        """
+        return self._sectPr.titlePg_val
+
+    @different_first_page_header_footer.setter
+    def different_first_page_header_footer(self, value: bool):
+        self._sectPr.titlePg_val = value
+
+    @property
+    def even_page_footer(self) -> _Footer:
+        """|_Footer| object defining footer content for even pages.
+
+        The content of this footer definition is ignored unless the document setting
+        :attr:`~.Settings.odd_and_even_pages_header_footer` is set True.
+        """
+        return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.EVEN_PAGE)
+
+    @property
+    def even_page_header(self) -> _Header:
+        """|_Header| object defining header content for even pages.
+
+        The content of this header definition is ignored unless the document setting
+        :attr:`~.Settings.odd_and_even_pages_header_footer` is set True.
+        """
+        return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.EVEN_PAGE)
+
+    @property
+    def first_page_footer(self) -> _Footer:
+        """|_Footer| object defining footer content for the first page of this section.
+
+        The content of this footer definition is ignored unless the property
+        :attr:`.different_first_page_header_footer` is set True.
+        """
+        return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.FIRST_PAGE)
+
+    @property
+    def first_page_header(self) -> _Header:
+        """|_Header| object defining header content for the first page of this section.
+
+        The content of this header definition is ignored unless the property
+        :attr:`.different_first_page_header_footer` is set True.
+        """
+        return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.FIRST_PAGE)
+
+    @lazyproperty
+    def footer(self) -> _Footer:
+        """|_Footer| object representing default page footer for this section.
+
+        The default footer is used for odd-numbered pages when separate odd/even footers
+        are enabled. It is used for both odd and even-numbered pages otherwise.
+        """
+        return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY)
+
+    @property
+    def footer_distance(self) -> Length | None:
+        """Distance from bottom edge of page to bottom edge of the footer.
+
+        Read/write. |None| if no setting is present in the XML.
+        """
+        return self._sectPr.footer
+
+    @footer_distance.setter
+    def footer_distance(self, value: int | Length | None):
+        self._sectPr.footer = value
+
+    @property
+    def gutter(self) -> Length | None:
+        """|Length| object representing page gutter size in English Metric Units.
+
+        Read/write. The page gutter is extra spacing added to the `inner` margin to
+        ensure even margins after page binding. Generally only used in book-bound
+        documents with double-sided and facing pages.
+
+        This setting applies to all pages in this section.
+
+        """
+        return self._sectPr.gutter
+
+    @gutter.setter
+    def gutter(self, value: int | Length | None):
+        self._sectPr.gutter = value
+
+    @lazyproperty
+    def header(self) -> _Header:
+        """|_Header| object representing default page header for this section.
+
+        The default header is used for odd-numbered pages when separate odd/even headers
+        are enabled. It is used for both odd and even-numbered pages otherwise.
+        """
+        return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY)
+
+    @property
+    def header_distance(self) -> Length | None:
+        """Distance from top edge of page to top edge of header.
+
+        Read/write. |None| if no setting is present in the XML. Assigning |None| causes
+        default value to be used.
+        """
+        return self._sectPr.header
+
+    @header_distance.setter
+    def header_distance(self, value: int | Length | None):
+        self._sectPr.header = value
+
+    def iter_inner_content(self) -> Iterator[Paragraph | Table]:
+        """Generate each Paragraph or Table object in this `section`.
+
+        Items appear in document order.
+        """
+        for element in self._sectPr.iter_inner_content():
+            yield (Paragraph(element, self) if isinstance(element, CT_P) else Table(element, self))
+
+    @property
+    def left_margin(self) -> Length | None:
+        """|Length| object representing the left margin for all pages in this section in
+        English Metric Units."""
+        return self._sectPr.left_margin
+
+    @left_margin.setter
+    def left_margin(self, value: int | Length | None):
+        self._sectPr.left_margin = value
+
+    @property
+    def orientation(self) -> WD_ORIENTATION:
+        """:ref:`WdOrientation` member specifying page orientation for this section.
+
+        One of ``WD_ORIENT.PORTRAIT`` or ``WD_ORIENT.LANDSCAPE``.
+        """
+        return self._sectPr.orientation
+
+    @orientation.setter
+    def orientation(self, value: WD_ORIENTATION | None):
+        self._sectPr.orientation = value
+
+    @property
+    def page_height(self) -> Length | None:
+        """Total page height used for this section.
+
+        This value is inclusive of all edge spacing values such as margins.
+
+        Page orientation is taken into account, so for example, its expected value
+        would be ``Inches(8.5)`` for letter-sized paper when orientation is landscape.
+        """
+        return self._sectPr.page_height
+
+    @page_height.setter
+    def page_height(self, value: Length | None):
+        self._sectPr.page_height = value
+
+    @property
+    def page_width(self) -> Length | None:
+        """Total page width used for this section.
+
+        This value is like "paper size" and includes all edge spacing values such as
+        margins.
+
+        Page orientation is taken into account, so for example, its expected value
+        would be ``Inches(11)`` for letter-sized paper when orientation is landscape.
+        """
+        return self._sectPr.page_width
+
+    @page_width.setter
+    def page_width(self, value: Length | None):
+        self._sectPr.page_width = value
+
+    @property
+    def part(self) -> StoryPart:
+        return self._document_part
+
+    @property
+    def right_margin(self) -> Length | None:
+        """|Length| object representing the right margin for all pages in this section
+        in English Metric Units."""
+        return self._sectPr.right_margin
+
+    @right_margin.setter
+    def right_margin(self, value: Length | None):
+        self._sectPr.right_margin = value
+
+    @property
+    def start_type(self) -> WD_SECTION_START:
+        """Type of page-break (if any) inserted at the start of this section.
+
+        For exmple, ``WD_SECTION_START.ODD_PAGE`` if the section should begin on the
+        next odd page, possibly inserting two page-breaks instead of one.
+        """
+        return self._sectPr.start_type
+
+    @start_type.setter
+    def start_type(self, value: WD_SECTION_START | None):
+        self._sectPr.start_type = value
+
+    @property
+    def top_margin(self) -> Length | None:
+        """|Length| object representing the top margin for all pages in this section in
+        English Metric Units."""
+        return self._sectPr.top_margin
+
+    @top_margin.setter
+    def top_margin(self, value: Length | None):
+        self._sectPr.top_margin = value
+
+
+class Sections(Sequence[Section]):
+    """Sequence of |Section| objects corresponding to the sections in the document.
+
+    Supports ``len()``, iteration, and indexed access.
+    """
+
+    def __init__(self, document_elm: CT_Document, document_part: DocumentPart):
+        super(Sections, self).__init__()
+        self._document_elm = document_elm
+        self._document_part = document_part
+
+    @overload
+    def __getitem__(self, key: int) -> Section: ...
+
+    @overload
+    def __getitem__(self, key: slice) -> List[Section]: ...
+
+    def __getitem__(self, key: int | slice) -> Section | List[Section]:
+        if isinstance(key, slice):
+            return [
+                Section(sectPr, self._document_part)
+                for sectPr in self._document_elm.sectPr_lst[key]
+            ]
+        return Section(self._document_elm.sectPr_lst[key], self._document_part)
+
+    def __iter__(self) -> Iterator[Section]:
+        for sectPr in self._document_elm.sectPr_lst:
+            yield Section(sectPr, self._document_part)
+
+    def __len__(self) -> int:
+        return len(self._document_elm.sectPr_lst)
+
+
+class _BaseHeaderFooter(BlockItemContainer):
+    """Base class for header and footer classes."""
+
+    def __init__(
+        self,
+        sectPr: CT_SectPr,
+        document_part: DocumentPart,
+        header_footer_index: WD_HEADER_FOOTER,
+    ):
+        self._sectPr = sectPr
+        self._document_part = document_part
+        self._hdrftr_index = header_footer_index
+
+    @property
+    def is_linked_to_previous(self) -> bool:
+        """``True`` if this header/footer uses the definition from the prior section.
+
+        ``False`` if this header/footer has an explicit definition.
+
+        Assigning ``True`` to this property removes the header/footer definition for
+        this section, causing it to "inherit" the corresponding definition of the prior
+        section. Assigning ``False`` causes a new, empty definition to be added for this
+        section, but only if no definition is already present.
+        """
+        # ---absence of a header/footer part indicates "linked" behavior---
+        return not self._has_definition
+
+    @is_linked_to_previous.setter
+    def is_linked_to_previous(self, value: bool) -> None:
+        new_state = bool(value)
+        # ---do nothing when value is not being changed---
+        if new_state == self.is_linked_to_previous:
+            return
+        if new_state is True:
+            self._drop_definition()
+        else:
+            self._add_definition()
+
+    @property
+    def part(self) -> HeaderPart | FooterPart:
+        """The |HeaderPart| or |FooterPart| for this header/footer.
+
+        This overrides `BlockItemContainer.part` and is required to support image
+        insertion and perhaps other content like hyperlinks.
+        """
+        # ---should not appear in documentation;
+        # ---not an interface property, even though public
+        return self._get_or_add_definition()
+
+    def _add_definition(self) -> HeaderPart | FooterPart:
+        """Return newly-added header/footer part."""
+        raise NotImplementedError("must be implemented by each subclass")
+
+    @property
+    def _definition(self) -> HeaderPart | FooterPart:
+        """|HeaderPart| or |FooterPart| object containing header/footer content."""
+        raise NotImplementedError("must be implemented by each subclass")
+
+    def _drop_definition(self) -> None:
+        """Remove header/footer part containing the definition of this header/footer."""
+        raise NotImplementedError("must be implemented by each subclass")
+
+    @property
+    def _element(self):
+        """`w:hdr` or `w:ftr` element, root of header/footer part."""
+        return self._get_or_add_definition().element
+
+    def _get_or_add_definition(self) -> HeaderPart | FooterPart:
+        """Return HeaderPart or FooterPart object for this section.
+
+        If this header/footer inherits its content, the part for the prior header/footer
+        is returned; this process continue recursively until a definition is found. If
+        the definition cannot be inherited (because the header/footer belongs to the
+        first section), a new definition is added for that first section and then
+        returned.
+        """
+        # ---note this method is called recursively to access inherited definitions---
+        # ---case-1: definition is not inherited---
+        if self._has_definition:
+            return self._definition
+        # ---case-2: definition is inherited and belongs to second-or-later section---
+        prior_headerfooter = self._prior_headerfooter
+        if prior_headerfooter:
+            return prior_headerfooter._get_or_add_definition()
+        # ---case-3: definition is inherited, but belongs to first section---
+        return self._add_definition()
+
+    @property
+    def _has_definition(self) -> bool:
+        """True if this header/footer has a related part containing its definition."""
+        raise NotImplementedError("must be implemented by each subclass")
+
+    @property
+    def _prior_headerfooter(self) -> _Header | _Footer | None:
+        """|_Header| or |_Footer| proxy on prior sectPr element.
+
+        Returns None if this is first section.
+        """
+        raise NotImplementedError("must be implemented by each subclass")
+
+
+class _Footer(_BaseHeaderFooter):
+    """Page footer, used for all three types (default, even-page, and first-page).
+
+    Note that, like a document or table cell, a footer must contain a minimum of one
+    paragraph and a new or otherwise "empty" footer contains a single empty paragraph.
+    This first paragraph can be accessed as `footer.paragraphs[0]` for purposes of
+    adding content to it. Using :meth:`add_paragraph()` by itself to add content will
+    leave an empty paragraph above the newly added one.
+    """
+
+    def _add_definition(self) -> FooterPart:
+        """Return newly-added footer part."""
+        footer_part, rId = self._document_part.add_footer_part()
+        self._sectPr.add_footerReference(self._hdrftr_index, rId)
+        return footer_part
+
+    @property
+    def _definition(self):
+        """|FooterPart| object containing content of this footer."""
+        footerReference = self._sectPr.get_footerReference(self._hdrftr_index)
+        # -- currently this is never called when `._has_definition` evaluates False --
+        assert footerReference is not None
+        return self._document_part.footer_part(footerReference.rId)
+
+    def _drop_definition(self):
+        """Remove footer definition (footer part) associated with this section."""
+        rId = self._sectPr.remove_footerReference(self._hdrftr_index)
+        self._document_part.drop_rel(rId)
+
+    @property
+    def _has_definition(self) -> bool:
+        """True if a footer is defined for this section."""
+        footerReference = self._sectPr.get_footerReference(self._hdrftr_index)
+        return footerReference is not None
+
+    @property
+    def _prior_headerfooter(self):
+        """|_Footer| proxy on prior sectPr element or None if this is first section."""
+        preceding_sectPr = self._sectPr.preceding_sectPr
+        return (
+            None
+            if preceding_sectPr is None
+            else _Footer(preceding_sectPr, self._document_part, self._hdrftr_index)
+        )
+
+
+class _Header(_BaseHeaderFooter):
+    """Page header, used for all three types (default, even-page, and first-page).
+
+    Note that, like a document or table cell, a header must contain a minimum of one
+    paragraph and a new or otherwise "empty" header contains a single empty paragraph.
+    This first paragraph can be accessed as `header.paragraphs[0]` for purposes of
+    adding content to it. Using :meth:`add_paragraph()` by itself to add content will
+    leave an empty paragraph above the newly added one.
+    """
+
+    def _add_definition(self):
+        """Return newly-added header part."""
+        header_part, rId = self._document_part.add_header_part()
+        self._sectPr.add_headerReference(self._hdrftr_index, rId)
+        return header_part
+
+    @property
+    def _definition(self):
+        """|HeaderPart| object containing content of this header."""
+        headerReference = self._sectPr.get_headerReference(self._hdrftr_index)
+        # -- currently this is never called when `._has_definition` evaluates False --
+        assert headerReference is not None
+        return self._document_part.header_part(headerReference.rId)
+
+    def _drop_definition(self):
+        """Remove header definition associated with this section."""
+        rId = self._sectPr.remove_headerReference(self._hdrftr_index)
+        self._document_part.drop_header_part(rId)
+
+    @property
+    def _has_definition(self) -> bool:
+        """True if a header is explicitly defined for this section."""
+        headerReference = self._sectPr.get_headerReference(self._hdrftr_index)
+        return headerReference is not None
+
+    @property
+    def _prior_headerfooter(self):
+        """|_Header| proxy on prior sectPr element or None if this is first section."""
+        preceding_sectPr = self._sectPr.preceding_sectPr
+        return (
+            None
+            if preceding_sectPr is None
+            else _Header(preceding_sectPr, self._document_part, self._hdrftr_index)
+        )