Skip to content
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

Added a:fld type to paragraphs for page numbers and datetimes #797

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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 pptx/oxml/__init__.py
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like these are redundant additions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the redundant lines

Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ def register_element_cls(nsptagname, cls):

from .text import ( # noqa: E402
CT_RegularTextRun,
CT_TextField,
CT_TextBody,
CT_TextBodyProperties,
CT_TextCharacterProperties,
Expand All @@ -474,6 +475,7 @@ def register_element_cls(nsptagname, cls):
register_element_cls("a:lnSpc", CT_TextSpacing)
register_element_cls("a:normAutofit", CT_TextNormalAutofit)
register_element_cls("a:r", CT_RegularTextRun)
register_element_cls("a:fld", CT_TextField)
register_element_cls("a:p", CT_TextParagraph)
register_element_cls("a:pPr", CT_TextParagraphProperties)
register_element_cls("c:rich", CT_TextBody)
Expand Down
3 changes: 3 additions & 0 deletions pptx/oxml/simpletypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -753,3 +753,6 @@ def convert_from_xml(cls, str_value):
}[units_part]
emu_value = Emu(int(round(quantity * multiplier)))
return emu_value

class ST_FieldType(XsdString):
pass
36 changes: 36 additions & 0 deletions pptx/oxml/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
ST_TextTypeface,
ST_TextWrappingType,
XsdBoolean,
XsdString,
ST_FieldType,
)
from pptx.oxml.xmlchemy import (
BaseOxmlElement,
Expand Down Expand Up @@ -324,8 +326,10 @@ class CT_TextField(BaseOxmlElement):
<a:fld> field element, for either a slide number or date field
"""

id = RequiredAttribute("id", XsdString)
Copy link

@ajkalb ajkalb Feb 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id is a python built-in function. Might use a different name.
Although maybe that makes functionality less obvious to user.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recognize that we are shadowing the id built-in, but since we are inside of a class, and are unlikely to need the id function here, I think the readability makes it worth it. There is also some precedent in the codebase for this: the CT_SlideId class in pptx/oxml/presentation.py, the CT_Connection class in pptx/oxml/shapes/connector.py, and CT_NonVisualDrawingProps in pptx/oxml/shapes/shared.py all have attributes named id.

rPr = ZeroOrOne("a:rPr", successors=("a:pPr", "a:t"))
t = ZeroOrOne("a:t", successors=())
type = OptionalAttribute("type", ST_FieldType)
Copy link

@ajkalb ajkalb Feb 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type is a python built-in function. Might use a different name.
Although maybe that makes functionality less obvious to user.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above re: id. There is a precedent for this as well in the CT_Placeholder class in pptx/oxml/shapes/shared.py


@property
def text(self):
Expand All @@ -338,6 +342,28 @@ def text(self):
text = t.text
return to_unicode(text) if text is not None else ""

@text.setter
def text(self, str):
"""*str* is unicode value to replace run text."""
t = self.t

if t is None:
self.get_or_add_t()

self.t.text = self._escape_ctrl_chars(str)

@staticmethod
def _escape_ctrl_chars(s):
"""Return str after replacing each control character with a plain-text escape.

For example, a BEL character (x07) would appear as "_x0007_". Horizontal-tab
(x09) and line-feed (x0A) are not escaped. All other characters in the range
x00-x1F are escaped.
"""
return re.sub(
r"([\x00-\x08\x0B-\x1F])", lambda match: "_x%04X_" % ord(match.group(1)), s
)


class CT_TextFont(BaseOxmlElement):
"""
Expand Down Expand Up @@ -379,6 +405,7 @@ class CT_TextParagraph(BaseOxmlElement):
pPr = ZeroOrOne("a:pPr", successors=("a:r", "a:br", "a:fld", "a:endParaRPr"))
r = ZeroOrMore("a:r", successors=("a:endParaRPr",))
br = ZeroOrMore("a:br", successors=("a:endParaRPr",))
fld = ZeroOrMore("a:fld", successors=("a:endParaRPr",))
endParaRPr = ZeroOrOne("a:endParaRPr", successors=())

def add_br(self):
Expand All @@ -387,11 +414,20 @@ def add_br(self):
"""
return self._add_br()

def add_fld(self, text=None):
f = self._add_fld()

if text:
f.text = text
return f


def add_r(self, text=None):
"""
Return a newly appended <a:r> element.
"""
r = self._add_r()

if text:
r.text = text
return r
Expand Down
44 changes: 44 additions & 0 deletions pptx/text/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,17 @@ def add_run(self):
r = self._p.add_r()
return _Run(r, self)

def add_field(self):
"""
Return a new field appended to the runs in this paragraph.
"""
import uuid;

f = self._p.add_fld()
f.id = f'{{{str(uuid.uuid4())}}}'

return _Field(f, self)

@property
def alignment(self):
"""
Expand Down Expand Up @@ -713,3 +724,36 @@ def text(self):
@text.setter
def text(self, str):
self._r.text = to_unicode(str)


class _Field(Subshape):
def __init__(self, f, parent):
super(_Field, self).__init__(parent)
self._f = f

@property
def text(self):
"""Read/write. A unicode string containing the text in this run.

Assignment replaces all text in the run. The assigned value can be a 7-bit ASCII
string, a UTF-8 encoded 8-bit string, or unicode. String values are converted to
unicode assuming UTF-8 encoding.

Any other control characters in the assigned string other than tab or newline
are escaped as a hex representation. For example, ESC (ASCII 27) is escaped as
"_x001B_". Contrast the behavior of `TextFrame.text` and `_Paragraph.text` with
respect to line-feed and vertical-tab characters.
"""
return self._f.text

@text.setter
def text(self, str):
self._f.text = to_unicode(str)

@property
def type(self):
return self._f.type

@type.setter
def type(self, str):
self._f.type = str