Skip to content

Provide api to access footnote, endnote #860

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
_scratch/
Session.vim
/.tox/
.vscode
venv
3 changes: 3 additions & 0 deletions docx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from docx.parts.numbering import NumberingPart
from docx.parts.settings import SettingsPart
from docx.parts.styles import StylesPart
from docx.parts.fntent import FootnotesPart, EndnotesPart


def part_class_selector(content_type, reltype):
Expand All @@ -33,6 +34,8 @@ def part_class_selector(content_type, reltype):
PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart
PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart
PartFactory.part_type_for[CT.WML_STYLES] = StylesPart
PartFactory.part_type_for[CT.WML_FOOTNOTES] = FootnotesPart
PartFactory.part_type_for[CT.WML_ENDNOTES] = EndnotesPart

del (
CT,
Expand Down
Empty file added docx/fntent/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions docx/fntent/endnoteReference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# encoding: utf-8

from __future__ import absolute_import, division, print_function, unicode_literals

from ..shared import Parented

class EndnoteReference(Parented):
"""
Proxy object wrapping ``<w:endnoteReference>`` element.
"""
def __init__(self, endnoteReference, parent):
super(EndnoteReference, self).__init__(parent)
self._element = endnoteReference

@property
def endnote(self):
return self.part.get_endnote(self._element.id)


129 changes: 129 additions & 0 deletions docx/fntent/fntent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# encoding: utf-8


from __future__ import absolute_import, division, print_function, unicode_literals

from docx.shared import ElementProxy
from ..text.paragraph import Paragraph
from ..shared import Parented

class Footnotes(ElementProxy):
"""
Footnotes object, container for all objects in the footnotes part

Accessed using the :attr:`.Document.footnotes` property. Supports ``len()``, iteration,
and dictionary-style access by footnote id.
"""

def __init__(self, element, part):
super(Footnotes, self).__init__(element)
self._part = part

@property
def part(self):
"""
The |FootnotesPart| object of this document.
"""
return self._part


@property
def footnotes(self):
return [Footnote(footnote, self) for footnote in self._element.footnote_lst]

def get_by_id(self, footnote_id):
"""Return the footnote matching *footnote_id*.

Returns |None| if not found.
"""
return self._get_by_id(footnote_id)

def _get_by_id(self, footnote_id):
"""
Return the footnote matching *footnote_id*.
"""
footnote = self._element.get_by_id(footnote_id)

if footnote is None:
return None

return Footnote(footnote, self)


class Footnote(Parented):
"""
Proxy object wrapping ``<w:footnote>`` element.
"""

def __init__(self, footnote, parent):
super(Footnote, self).__init__(parent)
self._element = footnote


@property
def paragraphs(self):
"""
Returns a list of paragraph proxy object
"""

return [Paragraph(p, self) for p in self._element.p_lst]


class Endnotes(ElementProxy):
"""
Endnotes object, container for all objects in the endnotes part

Accessed using the :attr:`.Document.endnotes` property. Supports ``len()``, iteration,
and dictionary-style access by endnote id.
"""

def __init__(self, element, part):
super(Endnotes, self).__init__(element)
self._part = part

@property
def part(self):
"""
The |EndnotesPart| object of this document.
"""
return self._part

@property
def endnotes(self):
return [Endnote(endnote, self) for endnote in self._element.endnote_lst]

def get_by_id(self, endnote_id):
"""Return the endnote matching *endnote_id*.

Returns |None| if not found.
"""
return self._get_by_id(endnote_id)

def _get_by_id(self, endnote_id):
"""
Return the endnote matching *endnote_id*.
"""
endnote = self._element.get_by_id(endnote_id)

if endnote is None:
return None

return Endnote(endnote, self)


class Endnote(Parented):
"""
Proxy object wrapping ``<w:endnote>`` element.
"""

def __init__(self, endnote, parent):
super(Endnote, self).__init__(parent)
self._element = endnote

@property
def paragraphs(self):
"""
Returns a list of paragraph proxy object
"""

return [Paragraph(p, self) for p in self._element.p_lst]
18 changes: 18 additions & 0 deletions docx/fntent/footnoteReference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# encoding: utf-8

from __future__ import absolute_import, division, print_function, unicode_literals

from ..shared import Parented

class FootnoteReference(Parented):
"""
Proxy object wrapping ``<w:footnoteReference>`` element.
"""
def __init__(self, footnoteReference, parent):
super(FootnoteReference, self).__init__(parent)
self._element = footnoteReference

@property
def footnote(self):
return self.part.get_footnote(self._element.id)

9 changes: 9 additions & 0 deletions docx/oxml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,12 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None):
register_element_cls('w:br', CT_Br)
register_element_cls('w:r', CT_R)
register_element_cls('w:t', CT_Text)


from .fntent import CT_Footnotes, CT_Footnote, CT_Endnotes, CT_Endnote, CT_FootnoteReference, CT_EndnoteReference
register_element_cls('w:footnote', CT_Footnote)
register_element_cls('w:footnotes', CT_Footnotes)
register_element_cls('w:endnote', CT_Endnote)
register_element_cls('w:endnotes', CT_Endnotes)
register_element_cls('w:footnoteReference', CT_FootnoteReference)
register_element_cls('w:endnoteReference', CT_EndnoteReference)
75 changes: 75 additions & 0 deletions docx/oxml/fntent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from .xmlchemy import (
BaseOxmlElement, OneAndOnlyOne, ZeroOrMore, OneOrMore, RequiredAttribute
)
from .simpletypes import ST_DecimalNumber, ST_OnOff, ST_String

class CT_Footnotes(BaseOxmlElement):
"""
A ``<w:footnotes>`` element, the root element of a footnotes part, i.e.
footnotes.xml
"""

footnote = ZeroOrMore('w:footnote')

def get_by_id(self, footnoteId):
"""
Return the ``<w:footnote>`` child element having ``w:id`` attribute
matching *footnoteId*, or |None| if not found.
"""
xpath = 'w:footnote[@w:id="%s"]' % footnoteId
try:
return self.xpath(xpath)[0]
except IndexError:
return None


class CT_Footnote(BaseOxmlElement):
"""
A ``<w:footnote>`` element, representing a footnote definition
"""

p = OneOrMore('w:p')

class CT_Endnotes(BaseOxmlElement):
"""
A ``<w:endnotes>`` element, the root element of a endnotes part, i.e.
endnotes.xml
"""

endnote = ZeroOrMore('w:endnote')

def get_by_id(self, endnoteId):
"""
Return the ``<w:endnote>`` child element having ``w:id`` attribute
matching *endnoteId*, or |None| if not found.
"""
xpath = 'w:endnote[@w:id="%s"]' % endnoteId
try:
return self.xpath(xpath)[0]
except IndexError:
return None



class CT_Endnote(BaseOxmlElement):
"""
A ``<w:endnote>`` element, representing a endnote definition
"""

p = OneOrMore('w:p')


class CT_FootnoteReference(BaseOxmlElement):
"""
A ``<w:footnoteReference>`` element. provide access to footnote proxy object.
"""

id = RequiredAttribute('w:id', ST_String)


class CT_EndnoteReference(BaseOxmlElement):
"""
A ``<w:endnoteReference>`` element. provide access to endnote proxy object.
"""

id = RequiredAttribute('w:id', ST_String)
2 changes: 2 additions & 0 deletions docx/oxml/text/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class CT_R(BaseOxmlElement):
cr = ZeroOrMore('w:cr')
tab = ZeroOrMore('w:tab')
drawing = ZeroOrMore('w:drawing')
footnoteReference = ZeroOrMore('w:footnoteReference')
endnoteReference = ZeroOrMore('w:endnoteReference')

def _insert_rPr(self, rPr):
self.insert(0, rPr)
Expand Down
57 changes: 57 additions & 0 deletions docx/parts/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from docx.document import Document
from docx.opc.constants import RELATIONSHIP_TYPE as RT
from docx.parts.hdrftr import FooterPart, HeaderPart
from docx.parts.fntent import FootnotesPart, EndnotesPart
from docx.parts.numbering import NumberingPart
from docx.parts.settings import SettingsPart
from docx.parts.story import BaseStoryPart
Expand Down Expand Up @@ -125,7 +126,37 @@ def styles(self):
of this document.
"""
return self._styles_part.styles

@property
def footnotes(self):
"""
A |Footnotes| object providing access to the footnotes in the footnotes part
of this document.
"""
return self._footnotes_part.footnotes

def get_footnote(self, footnote_id):
"""
Return the footnote matching *footnote_id*.
Returns |None| if no footnote matches *footnote_id*
"""
return self.footnotes.get_by_id(footnote_id)

@property
def endnotes(self):
"""
A |Endnotes| object providing access to the endnotes in the endnotes part
of this document.
"""
return self._endnotes_part.endnotes

def get_endnote(self, endnote_id):
"""
Return the endnote matching *endnote_id*.
Returns |None| if no endnote matches *endnote_id*
"""
return self.endnotes.get_by_id(endnote_id)

@property
def _settings_part(self):
"""
Expand All @@ -152,3 +183,29 @@ def _styles_part(self):
styles_part = StylesPart.default(self.package)
self.relate_to(styles_part, RT.STYLES)
return styles_part

@property
def _footnotes_part(self):
"""
Instance of |FootnotesPart| for this document. Creates an empty footnotes
part if one is not present.
"""
try:
return self.part_related_by(RT.FOOTNOTES)
except KeyError:
footnotes_part = FootnotesPart.default(self.package)
self.relate_to(footnotes_part, RT.FOOTNOTES)
return footnotes_part

@property
def _endnotes_part(self):
"""
Instance of |EndnotesPart| for this document. Creates an empty endnotes
part if one is not present.
"""
try:
return self.part_related_by(RT.ENDNOTES)
except KeyError:
endnotes_part = EndnotesPart.default(self.package)
self.relate_to(endnotes_part, RT.ENDNOTES)
return endnotes_part
Loading