aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/pptx/oxml/text.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pptx/oxml/text.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/pptx/oxml/text.py')
-rw-r--r--.venv/lib/python3.12/site-packages/pptx/oxml/text.py618
1 files changed, 618 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pptx/oxml/text.py b/.venv/lib/python3.12/site-packages/pptx/oxml/text.py
new file mode 100644
index 00000000..0f9ecc15
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pptx/oxml/text.py
@@ -0,0 +1,618 @@
+"""Custom element classes for text-related XML elements"""
+
+from __future__ import annotations
+
+import re
+from typing import TYPE_CHECKING, Callable, cast
+
+from pptx.enum.lang import MSO_LANGUAGE_ID
+from pptx.enum.text import (
+ MSO_AUTO_SIZE,
+ MSO_TEXT_UNDERLINE_TYPE,
+ MSO_VERTICAL_ANCHOR,
+ PP_PARAGRAPH_ALIGNMENT,
+)
+from pptx.exc import InvalidXmlError
+from pptx.oxml import parse_xml
+from pptx.oxml.dml.fill import CT_GradientFillProperties
+from pptx.oxml.ns import nsdecls
+from pptx.oxml.simpletypes import (
+ ST_Coordinate32,
+ ST_TextFontScalePercentOrPercentString,
+ ST_TextFontSize,
+ ST_TextIndentLevelType,
+ ST_TextSpacingPercentOrPercentString,
+ ST_TextSpacingPoint,
+ ST_TextTypeface,
+ ST_TextWrappingType,
+ XsdBoolean,
+)
+from pptx.oxml.xmlchemy import (
+ BaseOxmlElement,
+ Choice,
+ OneAndOnlyOne,
+ OneOrMore,
+ OptionalAttribute,
+ RequiredAttribute,
+ ZeroOrMore,
+ ZeroOrOne,
+ ZeroOrOneChoice,
+)
+from pptx.util import Emu, Length
+
+if TYPE_CHECKING:
+ from pptx.oxml.action import CT_Hyperlink
+
+
+class CT_RegularTextRun(BaseOxmlElement):
+ """`a:r` custom element class"""
+
+ get_or_add_rPr: Callable[[], CT_TextCharacterProperties]
+
+ rPr: CT_TextCharacterProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:rPr", successors=("a:t",)
+ )
+ t: BaseOxmlElement = OneAndOnlyOne("a:t") # pyright: ignore[reportAssignmentType]
+
+ @property
+ def text(self) -> str:
+ """All text of (required) `a:t` child."""
+ text = self.t.text
+ # -- t.text is None when t element is empty, e.g. '<a:t/>' --
+ return text or ""
+
+ @text.setter
+ def text(self, value: str): # pyright: ignore[reportIncompatibleMethodOverride]
+ self.t.text = self._escape_ctrl_chars(value)
+
+ @staticmethod
+ def _escape_ctrl_chars(s: str) -> str:
+ """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_TextBody(BaseOxmlElement):
+ """`p:txBody` custom element class.
+
+ Also used for `c:txPr` in charts and perhaps other elements.
+ """
+
+ add_p: Callable[[], CT_TextParagraph]
+ p_lst: list[CT_TextParagraph]
+
+ bodyPr: CT_TextBodyProperties = OneAndOnlyOne( # pyright: ignore[reportAssignmentType]
+ "a:bodyPr"
+ )
+ p: CT_TextParagraph = OneOrMore("a:p") # pyright: ignore[reportAssignmentType]
+
+ def clear_content(self):
+ """Remove all `a:p` children, but leave any others.
+
+ cf. lxml `_Element.clear()` method which removes all children.
+ """
+ for p in self.p_lst:
+ self.remove(p)
+
+ @property
+ def defRPr(self) -> CT_TextCharacterProperties:
+ """`a:defRPr` element of required first `p` child, added with its ancestors if not present.
+
+ Used when element is a ``c:txPr`` in a chart and the `p` element is used only to specify
+ formatting, not content.
+ """
+ p = self.p_lst[0]
+ pPr = p.get_or_add_pPr()
+ defRPr = pPr.get_or_add_defRPr()
+ return defRPr
+
+ @property
+ def is_empty(self) -> bool:
+ """True if only a single empty `a:p` element is present."""
+ ps = self.p_lst
+ if len(ps) > 1:
+ return False
+
+ if not ps:
+ raise InvalidXmlError("p:txBody must have at least one a:p")
+
+ if ps[0].text != "":
+ return False
+ return True
+
+ @classmethod
+ def new(cls):
+ """Return a new `p:txBody` element tree."""
+ xml = cls._txBody_tmpl()
+ txBody = parse_xml(xml)
+ return txBody
+
+ @classmethod
+ def new_a_txBody(cls) -> CT_TextBody:
+ """Return a new `a:txBody` element tree.
+
+ Suitable for use in a table cell and possibly other situations.
+ """
+ xml = cls._a_txBody_tmpl()
+ txBody = cast(CT_TextBody, parse_xml(xml))
+ return txBody
+
+ @classmethod
+ def new_p_txBody(cls):
+ """Return a new `p:txBody` element tree, suitable for use in an `p:sp` element."""
+ xml = cls._p_txBody_tmpl()
+ return parse_xml(xml)
+
+ @classmethod
+ def new_txPr(cls):
+ """Return a `c:txPr` element tree.
+
+ Suitable for use in a chart object like data labels or tick labels.
+ """
+ xml = (
+ "<c:txPr %s>\n"
+ " <a:bodyPr/>\n"
+ " <a:lstStyle/>\n"
+ " <a:p>\n"
+ " <a:pPr>\n"
+ " <a:defRPr/>\n"
+ " </a:pPr>\n"
+ " </a:p>\n"
+ "</c:txPr>\n"
+ ) % nsdecls("c", "a")
+ txPr = parse_xml(xml)
+ return txPr
+
+ def unclear_content(self):
+ """Ensure p:txBody has at least one a:p child.
+
+ Intuitively, reverse a ".clear_content()" operation to minimum conformance with spec
+ (single empty paragraph).
+ """
+ if len(self.p_lst) > 0:
+ return
+ self.add_p()
+
+ @classmethod
+ def _a_txBody_tmpl(cls):
+ return "<a:txBody %s>\n" " <a:bodyPr/>\n" " <a:p/>\n" "</a:txBody>\n" % (nsdecls("a"))
+
+ @classmethod
+ def _p_txBody_tmpl(cls):
+ return (
+ "<p:txBody %s>\n" " <a:bodyPr/>\n" " <a:p/>\n" "</p:txBody>\n" % (nsdecls("p", "a"))
+ )
+
+ @classmethod
+ def _txBody_tmpl(cls):
+ return (
+ "<p:txBody %s>\n"
+ " <a:bodyPr/>\n"
+ " <a:lstStyle/>\n"
+ " <a:p/>\n"
+ "</p:txBody>\n" % (nsdecls("a", "p"))
+ )
+
+
+class CT_TextBodyProperties(BaseOxmlElement):
+ """`a:bodyPr` custom element class."""
+
+ _add_noAutofit: Callable[[], BaseOxmlElement]
+ _add_normAutofit: Callable[[], CT_TextNormalAutofit]
+ _add_spAutoFit: Callable[[], BaseOxmlElement]
+ _remove_eg_textAutoFit: Callable[[], None]
+
+ noAutofit: BaseOxmlElement | None
+ normAutofit: CT_TextNormalAutofit | None
+ spAutoFit: BaseOxmlElement | None
+
+ eg_textAutoFit = ZeroOrOneChoice(
+ (Choice("a:noAutofit"), Choice("a:normAutofit"), Choice("a:spAutoFit")),
+ successors=("a:scene3d", "a:sp3d", "a:flatTx", "a:extLst"),
+ )
+ lIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "lIns", ST_Coordinate32, default=Emu(91440)
+ )
+ tIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "tIns", ST_Coordinate32, default=Emu(45720)
+ )
+ rIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "rIns", ST_Coordinate32, default=Emu(91440)
+ )
+ bIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "bIns", ST_Coordinate32, default=Emu(45720)
+ )
+ anchor: MSO_VERTICAL_ANCHOR | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "anchor", MSO_VERTICAL_ANCHOR
+ )
+ wrap: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "wrap", ST_TextWrappingType
+ )
+
+ @property
+ def autofit(self):
+ """The autofit setting for the text frame, a member of the `MSO_AUTO_SIZE` enumeration."""
+ if self.noAutofit is not None:
+ return MSO_AUTO_SIZE.NONE
+ if self.normAutofit is not None:
+ return MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
+ if self.spAutoFit is not None:
+ return MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT
+ return None
+
+ @autofit.setter
+ def autofit(self, value: MSO_AUTO_SIZE | None):
+ if value is not None and value not in MSO_AUTO_SIZE:
+ raise ValueError(
+ f"only None or a member of the MSO_AUTO_SIZE enumeration can be assigned to"
+ f" CT_TextBodyProperties.autofit, got {value}"
+ )
+ self._remove_eg_textAutoFit()
+ if value == MSO_AUTO_SIZE.NONE:
+ self._add_noAutofit()
+ elif value == MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE:
+ self._add_normAutofit()
+ elif value == MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT:
+ self._add_spAutoFit()
+
+
+class CT_TextCharacterProperties(BaseOxmlElement):
+ """Custom element class for `a:rPr`, `a:defRPr`, and `a:endParaRPr`.
+
+ 'rPr' is short for 'run properties', and it corresponds to the |Font| proxy class.
+ """
+
+ get_or_add_hlinkClick: Callable[[], CT_Hyperlink]
+ get_or_add_latin: Callable[[], CT_TextFont]
+ _remove_latin: Callable[[], None]
+ _remove_hlinkClick: Callable[[], None]
+
+ eg_fillProperties = ZeroOrOneChoice(
+ (
+ Choice("a:noFill"),
+ Choice("a:solidFill"),
+ Choice("a:gradFill"),
+ Choice("a:blipFill"),
+ Choice("a:pattFill"),
+ Choice("a:grpFill"),
+ ),
+ successors=(
+ "a:effectLst",
+ "a:effectDag",
+ "a:highlight",
+ "a:uLnTx",
+ "a:uLn",
+ "a:uFillTx",
+ "a:uFill",
+ "a:latin",
+ "a:ea",
+ "a:cs",
+ "a:sym",
+ "a:hlinkClick",
+ "a:hlinkMouseOver",
+ "a:rtl",
+ "a:extLst",
+ ),
+ )
+ latin: CT_TextFont | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:latin",
+ successors=(
+ "a:ea",
+ "a:cs",
+ "a:sym",
+ "a:hlinkClick",
+ "a:hlinkMouseOver",
+ "a:rtl",
+ "a:extLst",
+ ),
+ )
+ hlinkClick: CT_Hyperlink | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:hlinkClick", successors=("a:hlinkMouseOver", "a:rtl", "a:extLst")
+ )
+
+ lang: MSO_LANGUAGE_ID | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "lang", MSO_LANGUAGE_ID
+ )
+ sz: int | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "sz", ST_TextFontSize
+ )
+ b: bool | None = OptionalAttribute("b", XsdBoolean) # pyright: ignore[reportAssignmentType]
+ i: bool | None = OptionalAttribute("i", XsdBoolean) # pyright: ignore[reportAssignmentType]
+ u: MSO_TEXT_UNDERLINE_TYPE | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "u", MSO_TEXT_UNDERLINE_TYPE
+ )
+
+ def _new_gradFill(self):
+ return CT_GradientFillProperties.new_gradFill()
+
+ def add_hlinkClick(self, rId: str) -> CT_Hyperlink:
+ """Add an `a:hlinkClick` child element with r:id attribute set to `rId`."""
+ hlinkClick = self.get_or_add_hlinkClick()
+ hlinkClick.rId = rId
+ return hlinkClick
+
+
+class CT_TextField(BaseOxmlElement):
+ """`a:fld` field element, for either a slide number or date field."""
+
+ get_or_add_rPr: Callable[[], CT_TextCharacterProperties]
+
+ rPr: CT_TextCharacterProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:rPr", successors=("a:pPr", "a:t")
+ )
+ t: BaseOxmlElement | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:t", successors=()
+ )
+
+ @property
+ def text(self) -> str: # pyright: ignore[reportIncompatibleMethodOverride]
+ """The text of the `a:t` child element."""
+ t = self.t
+ if t is None:
+ return ""
+ return t.text or ""
+
+
+class CT_TextFont(BaseOxmlElement):
+ """Custom element class for `a:latin`, `a:ea`, `a:cs`, and `a:sym`.
+
+ These occur as child elements of CT_TextCharacterProperties, e.g. `a:rPr`.
+ """
+
+ typeface: str = RequiredAttribute( # pyright: ignore[reportAssignmentType]
+ "typeface", ST_TextTypeface
+ )
+
+
+class CT_TextLineBreak(BaseOxmlElement):
+ """`a:br` line break element"""
+
+ get_or_add_rPr: Callable[[], CT_TextCharacterProperties]
+
+ rPr = ZeroOrOne("a:rPr", successors=())
+
+ @property
+ def text(self): # pyright: ignore[reportIncompatibleMethodOverride]
+ """Unconditionally a single vertical-tab character.
+
+ A line break element can contain no text other than the implicit line feed it
+ represents.
+ """
+ return "\v"
+
+
+class CT_TextNormalAutofit(BaseOxmlElement):
+ """`a:normAutofit` element specifying fit text to shape font reduction, etc."""
+
+ fontScale = OptionalAttribute(
+ "fontScale", ST_TextFontScalePercentOrPercentString, default=100.0
+ )
+
+
+class CT_TextParagraph(BaseOxmlElement):
+ """`a:p` custom element class"""
+
+ get_or_add_endParaRPr: Callable[[], CT_TextCharacterProperties]
+ get_or_add_pPr: Callable[[], CT_TextParagraphProperties]
+ r_lst: list[CT_RegularTextRun]
+ _add_br: Callable[[], CT_TextLineBreak]
+ _add_r: Callable[[], CT_RegularTextRun]
+
+ pPr: CT_TextParagraphProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "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",))
+ endParaRPr: CT_TextCharacterProperties | None = ZeroOrOne(
+ "a:endParaRPr", successors=()
+ ) # pyright: ignore[reportAssignmentType]
+
+ def add_br(self) -> CT_TextLineBreak:
+ """Return a newly appended `a:br` element."""
+ return self._add_br()
+
+ def add_r(self, text: str | None = None) -> CT_RegularTextRun:
+ """Return a newly appended `a:r` element."""
+ r = self._add_r()
+ if text:
+ r.text = text
+ return r
+
+ def append_text(self, text: str):
+ """Append `a:r` and `a:br` elements to `p` based on `text`.
+
+ Any `\n` or `\v` (vertical-tab) characters in `text` delimit `a:r` (run) elements and
+ themselves are translated to `a:br` (line-break) elements. The vertical-tab character
+ appears in clipboard text from PowerPoint at "soft" line-breaks (new-line, but not new
+ paragraph).
+ """
+ for idx, r_str in enumerate(re.split("\n|\v", text)):
+ # ---breaks are only added _between_ items, not at start---
+ if idx > 0:
+ self.add_br()
+ # ---runs that would be empty are not added---
+ if r_str:
+ self.add_r(r_str)
+
+ @property
+ def content_children(self) -> tuple[CT_RegularTextRun | CT_TextLineBreak | CT_TextField, ...]:
+ """Sequence containing text-container child elements of this `a:p` element.
+
+ These include `a:r`, `a:br`, and `a:fld`.
+ """
+ return tuple(
+ e for e in self if isinstance(e, (CT_RegularTextRun, CT_TextLineBreak, CT_TextField))
+ )
+
+ @property
+ def text(self) -> str: # pyright: ignore[reportIncompatibleMethodOverride]
+ """str text contained in this paragraph."""
+ # ---note this shadows the lxml _Element.text---
+ return "".join([child.text for child in self.content_children])
+
+ def _new_r(self):
+ r_xml = "<a:r %s><a:t/></a:r>" % nsdecls("a")
+ return parse_xml(r_xml)
+
+
+class CT_TextParagraphProperties(BaseOxmlElement):
+ """`a:pPr` custom element class."""
+
+ get_or_add_defRPr: Callable[[], CT_TextCharacterProperties]
+ _add_lnSpc: Callable[[], CT_TextSpacing]
+ _add_spcAft: Callable[[], CT_TextSpacing]
+ _add_spcBef: Callable[[], CT_TextSpacing]
+ _remove_lnSpc: Callable[[], None]
+ _remove_spcAft: Callable[[], None]
+ _remove_spcBef: Callable[[], None]
+
+ _tag_seq = (
+ "a:lnSpc",
+ "a:spcBef",
+ "a:spcAft",
+ "a:buClrTx",
+ "a:buClr",
+ "a:buSzTx",
+ "a:buSzPct",
+ "a:buSzPts",
+ "a:buFontTx",
+ "a:buFont",
+ "a:buNone",
+ "a:buAutoNum",
+ "a:buChar",
+ "a:buBlip",
+ "a:tabLst",
+ "a:defRPr",
+ "a:extLst",
+ )
+ lnSpc: CT_TextSpacing | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:lnSpc", successors=_tag_seq[1:]
+ )
+ spcBef: CT_TextSpacing | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:spcBef", successors=_tag_seq[2:]
+ )
+ spcAft: CT_TextSpacing | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:spcAft", successors=_tag_seq[3:]
+ )
+ defRPr: CT_TextCharacterProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:defRPr", successors=_tag_seq[16:]
+ )
+ lvl: int = OptionalAttribute( # pyright: ignore[reportAssignmentType]
+ "lvl", ST_TextIndentLevelType, default=0
+ )
+ algn: PP_PARAGRAPH_ALIGNMENT | None = OptionalAttribute(
+ "algn", PP_PARAGRAPH_ALIGNMENT
+ ) # pyright: ignore[reportAssignmentType]
+ del _tag_seq
+
+ @property
+ def line_spacing(self) -> float | Length | None:
+ """The spacing between baselines of successive lines in this paragraph.
+
+ A float value indicates a number of lines. A |Length| value indicates a fixed spacing.
+ Value is contained in `./a:lnSpc/a:spcPts/@val` or `./a:lnSpc/a:spcPct/@val`. Value is
+ |None| if no element is present.
+ """
+ lnSpc = self.lnSpc
+ if lnSpc is None:
+ return None
+ if lnSpc.spcPts is not None:
+ return lnSpc.spcPts.val
+ return cast(CT_TextSpacingPercent, lnSpc.spcPct).val
+
+ @line_spacing.setter
+ def line_spacing(self, value: float | Length | None):
+ self._remove_lnSpc()
+ if value is None:
+ return
+ if isinstance(value, Length):
+ self._add_lnSpc().set_spcPts(value)
+ else:
+ self._add_lnSpc().set_spcPct(value)
+
+ @property
+ def space_after(self) -> Length | None:
+ """The EMU equivalent of the centipoints value in `./a:spcAft/a:spcPts/@val`."""
+ spcAft = self.spcAft
+ if spcAft is None:
+ return None
+ spcPts = spcAft.spcPts
+ if spcPts is None:
+ return None
+ return spcPts.val
+
+ @space_after.setter
+ def space_after(self, value: Length | None):
+ self._remove_spcAft()
+ if value is not None:
+ self._add_spcAft().set_spcPts(value)
+
+ @property
+ def space_before(self):
+ """The EMU equivalent of the centipoints value in `./a:spcBef/a:spcPts/@val`."""
+ spcBef = self.spcBef
+ if spcBef is None:
+ return None
+ spcPts = spcBef.spcPts
+ if spcPts is None:
+ return None
+ return spcPts.val
+
+ @space_before.setter
+ def space_before(self, value: Length | None):
+ self._remove_spcBef()
+ if value is not None:
+ self._add_spcBef().set_spcPts(value)
+
+
+class CT_TextSpacing(BaseOxmlElement):
+ """Used for `a:lnSpc`, `a:spcBef`, and `a:spcAft` elements."""
+
+ get_or_add_spcPct: Callable[[], CT_TextSpacingPercent]
+ get_or_add_spcPts: Callable[[], CT_TextSpacingPoint]
+ _remove_spcPct: Callable[[], None]
+ _remove_spcPts: Callable[[], None]
+
+ # this should actually be a OneAndOnlyOneChoice, but that's not
+ # implemented yet.
+ spcPct: CT_TextSpacingPercent | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:spcPct"
+ )
+ spcPts: CT_TextSpacingPoint | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
+ "a:spcPts"
+ )
+
+ def set_spcPct(self, value: float):
+ """Set spacing to `value` lines, e.g. 1.75 lines.
+
+ A ./a:spcPts child is removed if present.
+ """
+ self._remove_spcPts()
+ spcPct = self.get_or_add_spcPct()
+ spcPct.val = value
+
+ def set_spcPts(self, value: Length):
+ """Set spacing to `value` points. A ./a:spcPct child is removed if present."""
+ self._remove_spcPct()
+ spcPts = self.get_or_add_spcPts()
+ spcPts.val = value
+
+
+class CT_TextSpacingPercent(BaseOxmlElement):
+ """`a:spcPct` element, specifying spacing in thousandths of a percent in its `val` attribute."""
+
+ val: float = RequiredAttribute( # pyright: ignore[reportAssignmentType]
+ "val", ST_TextSpacingPercentOrPercentString
+ )
+
+
+class CT_TextSpacingPoint(BaseOxmlElement):
+ """`a:spcPts` element, specifying spacing in centipoints in its `val` attribute."""
+
+ val: Length = RequiredAttribute( # pyright: ignore[reportAssignmentType]
+ "val", ST_TextSpacingPoint
+ )