Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/openpyxl/cell/rich_text.py: 28%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

116 statements  

1# Copyright (c) 2010-2024 openpyxl 

2 

3""" 

4RichText definition 

5""" 

6from copy import copy 

7from openpyxl.compat import NUMERIC_TYPES 

8from openpyxl.cell.text import InlineFont, Text 

9from openpyxl.descriptors import ( 

10 Strict, 

11 String, 

12 Typed 

13) 

14 

15from openpyxl.xml.functions import Element, whitespace 

16 

17class TextBlock(Strict): 

18 """ Represents text string in a specific format 

19 

20 This class is used as part of constructing a rich text strings. 

21 """ 

22 font = Typed(expected_type=InlineFont) 

23 text = String() 

24 

25 def __init__(self, font, text): 

26 self.font = font 

27 self.text = text 

28 

29 

30 def __eq__(self, other): 

31 return self.text == other.text and self.font == other.font 

32 

33 

34 def __str__(self): 

35 """Just retun the text""" 

36 return self.text 

37 

38 

39 def __repr__(self): 

40 font = self.font != InlineFont() and self.font or "default" 

41 return f"{self.__class__.__name__} text={self.text}, font={font}" 

42 

43 

44 def to_tree(self): 

45 el = Element("r") 

46 el.append(self.font.to_tree(tagname="rPr")) 

47 t = Element("t") 

48 t.text = self.text 

49 whitespace(t) 

50 el.append(t) 

51 return el 

52 

53# 

54# Rich Text class. 

55# This class behaves just like a list whose members are either simple strings, or TextBlock() instances. 

56# In addition, it can be initialized in several ways: 

57# t = CellRFichText([...]) # initialize with a list. 

58# t = CellRFichText((...)) # initialize with a tuple. 

59# t = CellRichText(node) # where node is an Element() from either lxml or xml.etree (has a 'tag' element) 

60class CellRichText(list): 

61 """Represents a rich text string. 

62 

63 Initialize with a list made of pure strings or :class:`TextBlock` elements 

64 Can index object to access or modify individual rich text elements 

65 it also supports the + and += operators between rich text strings 

66 There are no user methods for this class 

67 

68 operations which modify the string will generally call an optimization pass afterwards, 

69 that merges text blocks with identical formats, consecutive pure text strings, 

70 and remove empty strings and empty text blocks 

71 """ 

72 

73 def __init__(self, *args): 

74 if len(args) == 1: 

75 args = args[0] 

76 if isinstance(args, (list, tuple)): 

77 CellRichText._check_rich_text(args) 

78 else: 

79 CellRichText._check_element(args) 

80 args = [args] 

81 else: 

82 CellRichText._check_rich_text(args) 

83 super().__init__(args) 

84 

85 

86 @classmethod 

87 def _check_element(cls, value): 

88 if not isinstance(value, (str, TextBlock, NUMERIC_TYPES)): 

89 raise TypeError(f"Illegal CellRichText element {value}") 

90 

91 

92 @classmethod 

93 def _check_rich_text(cls, rich_text): 

94 for t in rich_text: 

95 CellRichText._check_element(t) 

96 

97 @classmethod 

98 def from_tree(cls, node): 

99 text = Text.from_tree(node) 

100 if text.t: 

101 return (text.t.replace('x005F_', ''),) 

102 s = [] 

103 for r in text.r: 

104 t = "" 

105 if r.t: 

106 t = r.t.replace('x005F_', '') 

107 if r.rPr: 

108 s.append(TextBlock(r.rPr, t)) 

109 else: 

110 s.append(t) 

111 return cls(s) 

112 

113 # Merge TextBlocks with identical formatting 

114 # remove empty elements 

115 def _opt(self): 

116 last_t = None 

117 l = CellRichText(tuple()) 

118 for t in self: 

119 if isinstance(t, str): 

120 if not t: 

121 continue 

122 elif not t.text: 

123 continue 

124 if type(last_t) == type(t): 

125 if isinstance(t, str): 

126 last_t += t 

127 continue 

128 elif last_t.font == t.font: 

129 last_t.text += t.text 

130 continue 

131 if last_t: 

132 l.append(last_t) 

133 last_t = t 

134 if last_t: 

135 # Add remaining TextBlock at end of rich text 

136 l.append(last_t) 

137 super().__setitem__(slice(None), l) 

138 return self 

139 

140 

141 def __iadd__(self, arg): 

142 # copy used here to create new TextBlock() so we don't modify the right hand side in _opt() 

143 CellRichText._check_rich_text(arg) 

144 super().__iadd__([copy(e) for e in list(arg)]) 

145 return self._opt() 

146 

147 

148 def __add__(self, arg): 

149 return CellRichText([copy(e) for e in list(self) + list(arg)])._opt() 

150 

151 

152 def __setitem__(self, indx, val): 

153 CellRichText._check_element(val) 

154 super().__setitem__(indx, val) 

155 self._opt() 

156 

157 

158 def append(self, arg): 

159 CellRichText._check_element(arg) 

160 super().append(arg) 

161 

162 

163 def extend(self, arg): 

164 CellRichText._check_rich_text(arg) 

165 super().extend(arg) 

166 

167 

168 def __repr__(self): 

169 return "CellRichText([{}])".format(', '.join((repr(s) for s in self))) 

170 

171 

172 def __str__(self): 

173 return ''.join([str(s) for s in self]) 

174 

175 

176 def as_list(self): 

177 """ 

178 Returns a list of the strings contained. 

179 The main reason for this is to make editing easier. 

180 """ 

181 return [str(s) for s in self] 

182 

183 

184 def to_tree(self): 

185 """ 

186 Return the full XML representation 

187 """ 

188 container = Element("is") 

189 for obj in self: 

190 if isinstance(obj, TextBlock): 

191 container.append(obj.to_tree()) 

192 

193 else: 

194 el = Element("r") 

195 t = Element("t") 

196 t.text = obj 

197 whitespace(t) 

198 el.append(t) 

199 container.append(el) 

200 

201 return container 

202