|
1 # -*- coding: utf-8 -*- |
|
2 """ |
|
3 pygments.formatters.rtf |
|
4 ~~~~~~~~~~~~~~~~~~~~~~~ |
|
5 |
|
6 A formatter that generates RTF files. |
|
7 |
|
8 :copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS. |
|
9 :license: BSD, see LICENSE for details. |
|
10 """ |
|
11 |
|
12 from pygments.formatter import Formatter |
|
13 from pygments.util import get_int_opt, _surrogatepair |
|
14 |
|
15 |
|
16 __all__ = ['RtfFormatter'] |
|
17 |
|
18 |
|
19 class RtfFormatter(Formatter): |
|
20 """ |
|
21 Format tokens as RTF markup. This formatter automatically outputs full RTF |
|
22 documents with color information and other useful stuff. Perfect for Copy and |
|
23 Paste into Microsoft(R) Word(R) documents. |
|
24 |
|
25 Please note that ``encoding`` and ``outencoding`` options are ignored. |
|
26 The RTF format is ASCII natively, but handles unicode characters correctly |
|
27 thanks to escape sequences. |
|
28 |
|
29 .. versionadded:: 0.6 |
|
30 |
|
31 Additional options accepted: |
|
32 |
|
33 `style` |
|
34 The style to use, can be a string or a Style subclass (default: |
|
35 ``'default'``). |
|
36 |
|
37 `fontface` |
|
38 The used font famliy, for example ``Bitstream Vera Sans``. Defaults to |
|
39 some generic font which is supposed to have fixed width. |
|
40 |
|
41 `fontsize` |
|
42 Size of the font used. Size is specified in half points. The |
|
43 default is 24 half-points, giving a size 12 font. |
|
44 |
|
45 .. versionadded:: 2.0 |
|
46 """ |
|
47 name = 'RTF' |
|
48 aliases = ['rtf'] |
|
49 filenames = ['*.rtf'] |
|
50 |
|
51 def __init__(self, **options): |
|
52 r""" |
|
53 Additional options accepted: |
|
54 |
|
55 ``fontface`` |
|
56 Name of the font used. Could for example be ``'Courier New'`` |
|
57 to further specify the default which is ``'\fmodern'``. The RTF |
|
58 specification claims that ``\fmodern`` are "Fixed-pitch serif |
|
59 and sans serif fonts". Hope every RTF implementation thinks |
|
60 the same about modern... |
|
61 |
|
62 """ |
|
63 Formatter.__init__(self, **options) |
|
64 self.fontface = options.get('fontface') or '' |
|
65 self.fontsize = get_int_opt(options, 'fontsize', 0) |
|
66 |
|
67 def _escape(self, text): |
|
68 return text.replace(u'\\', u'\\\\') \ |
|
69 .replace(u'{', u'\\{') \ |
|
70 .replace(u'}', u'\\}') |
|
71 |
|
72 def _escape_text(self, text): |
|
73 # empty strings, should give a small performance improvment |
|
74 if not text: |
|
75 return u'' |
|
76 |
|
77 # escape text |
|
78 text = self._escape(text) |
|
79 |
|
80 buf = [] |
|
81 for c in text: |
|
82 cn = ord(c) |
|
83 if cn < (2**7): |
|
84 # ASCII character |
|
85 buf.append(str(c)) |
|
86 elif (2**7) <= cn < (2**16): |
|
87 # single unicode escape sequence |
|
88 buf.append(u'{\\u%d}' % cn) |
|
89 elif (2**16) <= cn: |
|
90 # RTF limits unicode to 16 bits. |
|
91 # Force surrogate pairs |
|
92 buf.append(u'{\\u%d}{\\u%d}' % _surrogatepair(cn)) |
|
93 |
|
94 return u''.join(buf).replace(u'\n', u'\\par\n') |
|
95 |
|
96 def format_unencoded(self, tokensource, outfile): |
|
97 # rtf 1.8 header |
|
98 outfile.write(u'{\\rtf1\\ansi\\uc0\\deff0' |
|
99 u'{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}' |
|
100 u'{\\colortbl;' % (self.fontface and |
|
101 u' ' + self._escape(self.fontface) or |
|
102 u'')) |
|
103 |
|
104 # convert colors and save them in a mapping to access them later. |
|
105 color_mapping = {} |
|
106 offset = 1 |
|
107 for _, style in self.style: |
|
108 for color in style['color'], style['bgcolor'], style['border']: |
|
109 if color and color not in color_mapping: |
|
110 color_mapping[color] = offset |
|
111 outfile.write(u'\\red%d\\green%d\\blue%d;' % ( |
|
112 int(color[0:2], 16), |
|
113 int(color[2:4], 16), |
|
114 int(color[4:6], 16) |
|
115 )) |
|
116 offset += 1 |
|
117 outfile.write(u'}\\f0 ') |
|
118 if self.fontsize: |
|
119 outfile.write(u'\\fs%d' % (self.fontsize)) |
|
120 |
|
121 # highlight stream |
|
122 for ttype, value in tokensource: |
|
123 while not self.style.styles_token(ttype) and ttype.parent: |
|
124 ttype = ttype.parent |
|
125 style = self.style.style_for_token(ttype) |
|
126 buf = [] |
|
127 if style['bgcolor']: |
|
128 buf.append(u'\\cb%d' % color_mapping[style['bgcolor']]) |
|
129 if style['color']: |
|
130 buf.append(u'\\cf%d' % color_mapping[style['color']]) |
|
131 if style['bold']: |
|
132 buf.append(u'\\b') |
|
133 if style['italic']: |
|
134 buf.append(u'\\i') |
|
135 if style['underline']: |
|
136 buf.append(u'\\ul') |
|
137 if style['border']: |
|
138 buf.append(u'\\chbrdr\\chcfpat%d' % |
|
139 color_mapping[style['border']]) |
|
140 start = u''.join(buf) |
|
141 if start: |
|
142 outfile.write(u'{%s ' % start) |
|
143 outfile.write(self._escape_text(value)) |
|
144 if start: |
|
145 outfile.write(u'}') |
|
146 |
|
147 outfile.write(u'}') |