You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
4.8 KiB

# encoding: utf-8
"""
Custom element classes related to text runs (CT_R).
"""
from ..ns import qn
from ..simpletypes import ST_BrClear, ST_BrType
from ..xmlchemy import (
BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne
)
class CT_Br(BaseOxmlElement):
"""
``<w:br>`` element, indicating a line, page, or column break in a run.
"""
type = OptionalAttribute('w:type', ST_BrType)
clear = OptionalAttribute('w:clear', ST_BrClear)
class CT_R(BaseOxmlElement):
"""
``<w:r>`` element, containing the properties and text for a run.
"""
rPr = ZeroOrOne('w:rPr')
t = ZeroOrMore('w:t')
br = ZeroOrMore('w:br')
cr = ZeroOrMore('w:cr')
tab = ZeroOrMore('w:tab')
drawing = ZeroOrMore('w:drawing')
def _insert_rPr(self, rPr):
self.insert(0, rPr)
return rPr
def add_t(self, text):
"""
Return a newly added ``<w:t>`` element containing *text*.
"""
t = self._add_t(text=text)
if len(text.strip()) < len(text):
t.set(qn('xml:space'), 'preserve')
return t
def add_drawing(self, inline_or_anchor):
"""
Return a newly appended ``CT_Drawing`` (``<w:drawing>``) child
element having *inline_or_anchor* as its child.
"""
drawing = self._add_drawing()
drawing.append(inline_or_anchor)
return drawing
def clear_content(self):
"""
Remove all child elements except the ``<w:rPr>`` element if present.
"""
content_child_elms = self[1:] if self.rPr is not None else self[:]
for child in content_child_elms:
self.remove(child)
@property
def style(self):
"""
String contained in w:val attribute of <w:rStyle> grandchild, or
|None| if that element is not present.
"""
rPr = self.rPr
if rPr is None:
return None
return rPr.style
@style.setter
def style(self, style):
"""
Set the character style of this <w:r> element to *style*. If *style*
is None, remove the style element.
"""
rPr = self.get_or_add_rPr()
rPr.style = style
@property
def text(self):
"""
A string representing the textual content of this run, with content
child elements like ``<w:tab/>`` translated to their Python
equivalent.
"""
text = ''
for child in self:
if child.tag == qn('w:t'):
t_text = child.text
text += t_text if t_text is not None else ''
elif child.tag == qn('w:tab'):
text += '\t'
elif child.tag in (qn('w:br'), qn('w:cr')):
text += '\n'
return text
@text.setter
def text(self, text):
self.clear_content()
_RunContentAppender.append_to_run_from_text(self, text)
class CT_Text(BaseOxmlElement):
"""
``<w:t>`` element, containing a sequence of characters within a run.
"""
class _RunContentAppender(object):
"""
Service object that knows how to translate a Python string into run
content elements appended to a specified ``<w:r>`` element. Contiguous
sequences of regular characters are appended in a single ``<w:t>``
element. Each tab character ('\t') causes a ``<w:tab/>`` element to be
appended. Likewise a newline or carriage return character ('\n', '\r')
causes a ``<w:cr>`` element to be appended.
"""
def __init__(self, r):
self._r = r
self._bfr = []
@classmethod
def append_to_run_from_text(cls, r, text):
"""
Create a "one-shot" ``_RunContentAppender`` instance and use it to
append the run content elements corresponding to *text* to the
``<w:r>`` element *r*.
"""
appender = cls(r)
appender.add_text(text)
def add_text(self, text):
"""
Append the run content elements corresponding to *text* to the
``<w:r>`` element of this instance.
"""
for char in text:
self.add_char(char)
self.flush()
def add_char(self, char):
"""
Process the next character of input through the translation finite
state maching (FSM). There are two possible states, buffer pending
and not pending, but those are hidden behind the ``.flush()`` method
which must be called at the end of text to ensure any pending
``<w:t>`` element is written.
"""
if char == '\t':
self.flush()
self._r.add_tab()
elif char in '\r\n':
self.flush()
self._r.add_br()
else:
self._bfr.append(char)
def flush(self):
text = ''.join(self._bfr)
if text:
self._r.add_t(text)
del self._bfr[:]