1# encoding: utf-8 2 3""" 4Custom element classes related to text runs (CT_R). 5""" 6 7from ..ns import qn 8from ..simpletypes import ST_BrClear, ST_BrType 9from ..xmlchemy import ( 10 BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne 11) 12 13 14class CT_Br(BaseOxmlElement): 15 """ 16 ``<w:br>`` element, indicating a line, page, or column break in a run. 17 """ 18 type = OptionalAttribute('w:type', ST_BrType) 19 clear = OptionalAttribute('w:clear', ST_BrClear) 20 21 22class CT_R(BaseOxmlElement): 23 """ 24 ``<w:r>`` element, containing the properties and text for a run. 25 """ 26 rPr = ZeroOrOne('w:rPr') 27 t = ZeroOrMore('w:t') 28 br = ZeroOrMore('w:br') 29 cr = ZeroOrMore('w:cr') 30 tab = ZeroOrMore('w:tab') 31 drawing = ZeroOrMore('w:drawing') 32 33 def _insert_rPr(self, rPr): 34 self.insert(0, rPr) 35 return rPr 36 37 def add_t(self, text): 38 """ 39 Return a newly added ``<w:t>`` element containing *text*. 40 """ 41 t = self._add_t(text=text) 42 if len(text.strip()) < len(text): 43 t.set(qn('xml:space'), 'preserve') 44 return t 45 46 def add_drawing(self, inline_or_anchor): 47 """ 48 Return a newly appended ``CT_Drawing`` (``<w:drawing>``) child 49 element having *inline_or_anchor* as its child. 50 """ 51 drawing = self._add_drawing() 52 drawing.append(inline_or_anchor) 53 return drawing 54 55 def clear_content(self): 56 """ 57 Remove all child elements except the ``<w:rPr>`` element if present. 58 """ 59 content_child_elms = self[1:] if self.rPr is not None else self[:] 60 for child in content_child_elms: 61 self.remove(child) 62 63 @property 64 def style(self): 65 """ 66 String contained in w:val attribute of <w:rStyle> grandchild, or 67 |None| if that element is not present. 68 """ 69 rPr = self.rPr 70 if rPr is None: 71 return None 72 return rPr.style 73 74 @style.setter 75 def style(self, style): 76 """ 77 Set the character style of this <w:r> element to *style*. If *style* 78 is None, remove the style element. 79 """ 80 rPr = self.get_or_add_rPr() 81 rPr.style = style 82 83 @property 84 def text(self): 85 """ 86 A string representing the textual content of this run, with content 87 child elements like ``<w:tab/>`` translated to their Python 88 equivalent. 89 """ 90 text = '' 91 for child in self: 92 if child.tag == qn('w:t'): 93 t_text = child.text 94 text += t_text if t_text is not None else '' 95 elif child.tag == qn('w:tab'): 96 text += '\t' 97 elif child.tag in (qn('w:br'), qn('w:cr')): 98 text += '\n' 99 return text 100 101 @text.setter 102 def text(self, text): 103 self.clear_content() 104 _RunContentAppender.append_to_run_from_text(self, text) 105 106 107class CT_Text(BaseOxmlElement): 108 """ 109 ``<w:t>`` element, containing a sequence of characters within a run. 110 """ 111 112 113class _RunContentAppender(object): 114 """ 115 Service object that knows how to translate a Python string into run 116 content elements appended to a specified ``<w:r>`` element. Contiguous 117 sequences of regular characters are appended in a single ``<w:t>`` 118 element. Each tab character ('\t') causes a ``<w:tab/>`` element to be 119 appended. Likewise a newline or carriage return character ('\n', '\r') 120 causes a ``<w:cr>`` element to be appended. 121 """ 122 def __init__(self, r): 123 self._r = r 124 self._bfr = [] 125 126 @classmethod 127 def append_to_run_from_text(cls, r, text): 128 """ 129 Create a "one-shot" ``_RunContentAppender`` instance and use it to 130 append the run content elements corresponding to *text* to the 131 ``<w:r>`` element *r*. 132 """ 133 appender = cls(r) 134 appender.add_text(text) 135 136 def add_text(self, text): 137 """ 138 Append the run content elements corresponding to *text* to the 139 ``<w:r>`` element of this instance. 140 """ 141 for char in text: 142 self.add_char(char) 143 self.flush() 144 145 def add_char(self, char): 146 """ 147 Process the next character of input through the translation finite 148 state maching (FSM). There are two possible states, buffer pending 149 and not pending, but those are hidden behind the ``.flush()`` method 150 which must be called at the end of text to ensure any pending 151 ``<w:t>`` element is written. 152 """ 153 if char == '\t': 154 self.flush() 155 self._r.add_tab() 156 elif char in '\r\n': 157 self.flush() 158 self._r.add_br() 159 else: 160 self._bfr.append(char) 161 162 def flush(self): 163 text = ''.join(self._bfr) 164 if text: 165 self._r.add_t(text) 166 del self._bfr[:] 167