Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pygments/formatters/rtf.py: 95%

64 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 07:45 +0000

1""" 

2 pygments.formatters.rtf 

3 ~~~~~~~~~~~~~~~~~~~~~~~ 

4 

5 A formatter that generates RTF files. 

6 

7 :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11from pygments.formatter import Formatter 

12from pygments.util import get_int_opt, surrogatepair 

13 

14 

15__all__ = ['RtfFormatter'] 

16 

17 

18class RtfFormatter(Formatter): 

19 """ 

20 Format tokens as RTF markup. This formatter automatically outputs full RTF 

21 documents with color information and other useful stuff. Perfect for Copy and 

22 Paste into Microsoft(R) Word(R) documents. 

23 

24 Please note that ``encoding`` and ``outencoding`` options are ignored. 

25 The RTF format is ASCII natively, but handles unicode characters correctly 

26 thanks to escape sequences. 

27 

28 .. versionadded:: 0.6 

29 

30 Additional options accepted: 

31 

32 `style` 

33 The style to use, can be a string or a Style subclass (default: 

34 ``'default'``). 

35 

36 `fontface` 

37 The used font family, for example ``Bitstream Vera Sans``. Defaults to 

38 some generic font which is supposed to have fixed width. 

39 

40 `fontsize` 

41 Size of the font used. Size is specified in half points. The 

42 default is 24 half-points, giving a size 12 font. 

43 

44 .. versionadded:: 2.0 

45 """ 

46 name = 'RTF' 

47 aliases = ['rtf'] 

48 filenames = ['*.rtf'] 

49 

50 def __init__(self, **options): 

51 r""" 

52 Additional options accepted: 

53 

54 ``fontface`` 

55 Name of the font used. Could for example be ``'Courier New'`` 

56 to further specify the default which is ``'\fmodern'``. The RTF 

57 specification claims that ``\fmodern`` are "Fixed-pitch serif 

58 and sans serif fonts". Hope every RTF implementation thinks 

59 the same about modern... 

60 

61 """ 

62 Formatter.__init__(self, **options) 

63 self.fontface = options.get('fontface') or '' 

64 self.fontsize = get_int_opt(options, 'fontsize', 0) 

65 

66 def _escape(self, text): 

67 return text.replace('\\', '\\\\') \ 

68 .replace('{', '\\{') \ 

69 .replace('}', '\\}') 

70 

71 def _escape_text(self, text): 

72 # empty strings, should give a small performance improvement 

73 if not text: 

74 return '' 

75 

76 # escape text 

77 text = self._escape(text) 

78 

79 buf = [] 

80 for c in text: 

81 cn = ord(c) 

82 if cn < (2**7): 

83 # ASCII character 

84 buf.append(str(c)) 

85 elif (2**7) <= cn < (2**16): 

86 # single unicode escape sequence 

87 buf.append('{\\u%d}' % cn) 

88 elif (2**16) <= cn: 

89 # RTF limits unicode to 16 bits. 

90 # Force surrogate pairs 

91 buf.append('{\\u%d}{\\u%d}' % surrogatepair(cn)) 

92 

93 return ''.join(buf).replace('\n', '\\par\n') 

94 

95 def format_unencoded(self, tokensource, outfile): 

96 # rtf 1.8 header 

97 outfile.write('{\\rtf1\\ansi\\uc0\\deff0' 

98 '{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}' 

99 '{\\colortbl;' % (self.fontface and 

100 ' ' + self._escape(self.fontface) or 

101 '')) 

102 

103 # convert colors and save them in a mapping to access them later. 

104 color_mapping = {} 

105 offset = 1 

106 for _, style in self.style: 

107 for color in style['color'], style['bgcolor'], style['border']: 

108 if color and color not in color_mapping: 

109 color_mapping[color] = offset 

110 outfile.write('\\red%d\\green%d\\blue%d;' % ( 

111 int(color[0:2], 16), 

112 int(color[2:4], 16), 

113 int(color[4:6], 16) 

114 )) 

115 offset += 1 

116 outfile.write('}\\f0 ') 

117 if self.fontsize: 

118 outfile.write('\\fs%d' % self.fontsize) 

119 

120 # highlight stream 

121 for ttype, value in tokensource: 

122 while not self.style.styles_token(ttype) and ttype.parent: 

123 ttype = ttype.parent 

124 style = self.style.style_for_token(ttype) 

125 buf = [] 

126 if style['bgcolor']: 

127 buf.append('\\cb%d' % color_mapping[style['bgcolor']]) 

128 if style['color']: 

129 buf.append('\\cf%d' % color_mapping[style['color']]) 

130 if style['bold']: 

131 buf.append('\\b') 

132 if style['italic']: 

133 buf.append('\\i') 

134 if style['underline']: 

135 buf.append('\\ul') 

136 if style['border']: 

137 buf.append('\\chbrdr\\chcfpat%d' % 

138 color_mapping[style['border']]) 

139 start = ''.join(buf) 

140 if start: 

141 outfile.write('{%s ' % start) 

142 outfile.write(self._escape_text(value)) 

143 if start: 

144 outfile.write('}') 

145 

146 outfile.write('}')