3 pygments.formatters.rtf |
3 pygments.formatters.rtf |
4 ~~~~~~~~~~~~~~~~~~~~~~~ |
4 ~~~~~~~~~~~~~~~~~~~~~~~ |
5 |
5 |
6 A formatter that generates RTF files. |
6 A formatter that generates RTF files. |
7 |
7 |
8 :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS. |
8 :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. |
9 :license: BSD, see LICENSE for details. |
9 :license: BSD, see LICENSE for details. |
10 """ |
10 """ |
11 |
11 |
12 from pygments.formatter import Formatter |
12 from pygments.formatter import Formatter |
13 from pygments.util import get_int_opt, _surrogatepair |
13 from pygments.util import get_int_opt, surrogatepair |
14 |
14 |
15 |
15 |
16 __all__ = ['RtfFormatter'] |
16 __all__ = ['RtfFormatter'] |
17 |
17 |
18 |
18 |
63 Formatter.__init__(self, **options) |
63 Formatter.__init__(self, **options) |
64 self.fontface = options.get('fontface') or '' |
64 self.fontface = options.get('fontface') or '' |
65 self.fontsize = get_int_opt(options, 'fontsize', 0) |
65 self.fontsize = get_int_opt(options, 'fontsize', 0) |
66 |
66 |
67 def _escape(self, text): |
67 def _escape(self, text): |
68 return text.replace(u'\\', u'\\\\') \ |
68 return text.replace('\\', '\\\\') \ |
69 .replace(u'{', u'\\{') \ |
69 .replace('{', '\\{') \ |
70 .replace(u'}', u'\\}') |
70 .replace('}', '\\}') |
71 |
71 |
72 def _escape_text(self, text): |
72 def _escape_text(self, text): |
73 # empty strings, should give a small performance improvement |
73 # empty strings, should give a small performance improvement |
74 if not text: |
74 if not text: |
75 return u'' |
75 return '' |
76 |
76 |
77 # escape text |
77 # escape text |
78 text = self._escape(text) |
78 text = self._escape(text) |
79 |
79 |
80 buf = [] |
80 buf = [] |
83 if cn < (2**7): |
83 if cn < (2**7): |
84 # ASCII character |
84 # ASCII character |
85 buf.append(str(c)) |
85 buf.append(str(c)) |
86 elif (2**7) <= cn < (2**16): |
86 elif (2**7) <= cn < (2**16): |
87 # single unicode escape sequence |
87 # single unicode escape sequence |
88 buf.append(u'{\\u%d}' % cn) |
88 buf.append('{\\u%d}' % cn) |
89 elif (2**16) <= cn: |
89 elif (2**16) <= cn: |
90 # RTF limits unicode to 16 bits. |
90 # RTF limits unicode to 16 bits. |
91 # Force surrogate pairs |
91 # Force surrogate pairs |
92 buf.append(u'{\\u%d}{\\u%d}' % _surrogatepair(cn)) |
92 buf.append('{\\u%d}{\\u%d}' % surrogatepair(cn)) |
93 |
93 |
94 return u''.join(buf).replace(u'\n', u'\\par\n') |
94 return ''.join(buf).replace('\n', '\\par\n') |
95 |
95 |
96 def format_unencoded(self, tokensource, outfile): |
96 def format_unencoded(self, tokensource, outfile): |
97 # rtf 1.8 header |
97 # rtf 1.8 header |
98 outfile.write(u'{\\rtf1\\ansi\\uc0\\deff0' |
98 outfile.write('{\\rtf1\\ansi\\uc0\\deff0' |
99 u'{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}' |
99 '{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}' |
100 u'{\\colortbl;' % (self.fontface and |
100 '{\\colortbl;' % (self.fontface and |
101 u' ' + self._escape(self.fontface) or |
101 ' ' + self._escape(self.fontface) or |
102 u'')) |
102 '')) |
103 |
103 |
104 # convert colors and save them in a mapping to access them later. |
104 # convert colors and save them in a mapping to access them later. |
105 color_mapping = {} |
105 color_mapping = {} |
106 offset = 1 |
106 offset = 1 |
107 for _, style in self.style: |
107 for _, style in self.style: |
108 for color in style['color'], style['bgcolor'], style['border']: |
108 for color in style['color'], style['bgcolor'], style['border']: |
109 if color and color not in color_mapping: |
109 if color and color not in color_mapping: |
110 color_mapping[color] = offset |
110 color_mapping[color] = offset |
111 outfile.write(u'\\red%d\\green%d\\blue%d;' % ( |
111 outfile.write('\\red%d\\green%d\\blue%d;' % ( |
112 int(color[0:2], 16), |
112 int(color[0:2], 16), |
113 int(color[2:4], 16), |
113 int(color[2:4], 16), |
114 int(color[4:6], 16) |
114 int(color[4:6], 16) |
115 )) |
115 )) |
116 offset += 1 |
116 offset += 1 |
117 outfile.write(u'}\\f0 ') |
117 outfile.write('}\\f0 ') |
118 if self.fontsize: |
118 if self.fontsize: |
119 outfile.write(u'\\fs%d' % (self.fontsize)) |
119 outfile.write('\\fs%d' % self.fontsize) |
120 |
120 |
121 # highlight stream |
121 # highlight stream |
122 for ttype, value in tokensource: |
122 for ttype, value in tokensource: |
123 while not self.style.styles_token(ttype) and ttype.parent: |
123 while not self.style.styles_token(ttype) and ttype.parent: |
124 ttype = ttype.parent |
124 ttype = ttype.parent |
125 style = self.style.style_for_token(ttype) |
125 style = self.style.style_for_token(ttype) |
126 buf = [] |
126 buf = [] |
127 if style['bgcolor']: |
127 if style['bgcolor']: |
128 buf.append(u'\\cb%d' % color_mapping[style['bgcolor']]) |
128 buf.append('\\cb%d' % color_mapping[style['bgcolor']]) |
129 if style['color']: |
129 if style['color']: |
130 buf.append(u'\\cf%d' % color_mapping[style['color']]) |
130 buf.append('\\cf%d' % color_mapping[style['color']]) |
131 if style['bold']: |
131 if style['bold']: |
132 buf.append(u'\\b') |
132 buf.append('\\b') |
133 if style['italic']: |
133 if style['italic']: |
134 buf.append(u'\\i') |
134 buf.append('\\i') |
135 if style['underline']: |
135 if style['underline']: |
136 buf.append(u'\\ul') |
136 buf.append('\\ul') |
137 if style['border']: |
137 if style['border']: |
138 buf.append(u'\\chbrdr\\chcfpat%d' % |
138 buf.append('\\chbrdr\\chcfpat%d' % |
139 color_mapping[style['border']]) |
139 color_mapping[style['border']]) |
140 start = u''.join(buf) |
140 start = ''.join(buf) |
141 if start: |
141 if start: |
142 outfile.write(u'{%s ' % start) |
142 outfile.write('{%s ' % start) |
143 outfile.write(self._escape_text(value)) |
143 outfile.write(self._escape_text(value)) |
144 if start: |
144 if start: |
145 outfile.write(u'}') |
145 outfile.write('}') |
146 |
146 |
147 outfile.write(u'}') |
147 outfile.write('}') |