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
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 07:45 +0000
1"""
2 pygments.formatters.rtf
3 ~~~~~~~~~~~~~~~~~~~~~~~
5 A formatter that generates RTF files.
7 :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9"""
11from pygments.formatter import Formatter
12from pygments.util import get_int_opt, surrogatepair
15__all__ = ['RtfFormatter']
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.
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.
28 .. versionadded:: 0.6
30 Additional options accepted:
32 `style`
33 The style to use, can be a string or a Style subclass (default:
34 ``'default'``).
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.
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.
44 .. versionadded:: 2.0
45 """
46 name = 'RTF'
47 aliases = ['rtf']
48 filenames = ['*.rtf']
50 def __init__(self, **options):
51 r"""
52 Additional options accepted:
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...
61 """
62 Formatter.__init__(self, **options)
63 self.fontface = options.get('fontface') or ''
64 self.fontsize = get_int_opt(options, 'fontsize', 0)
66 def _escape(self, text):
67 return text.replace('\\', '\\\\') \
68 .replace('{', '\\{') \
69 .replace('}', '\\}')
71 def _escape_text(self, text):
72 # empty strings, should give a small performance improvement
73 if not text:
74 return ''
76 # escape text
77 text = self._escape(text)
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))
93 return ''.join(buf).replace('\n', '\\par\n')
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 ''))
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)
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('}')
146 outfile.write('}')