|
1 # -*- coding: utf-8 -*- |
|
2 """ |
|
3 pygments.formatters.rtf |
|
4 ~~~~~~~~~~~~~~~~~~~~~~~ |
|
5 |
|
6 A formatter that generates RTF files. |
|
7 |
|
8 :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. |
|
9 :license: BSD, see LICENSE for details. |
|
10 """ |
|
11 |
|
12 from pygments.formatter import Formatter |
|
13 |
|
14 |
|
15 __all__ = ['RtfFormatter'] |
|
16 |
|
17 |
|
18 class 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® Word® documents. |
|
23 |
|
24 *New in Pygments 0.6.* |
|
25 |
|
26 Additional options accepted: |
|
27 |
|
28 `style` |
|
29 The style to use, can be a string or a Style subclass (default: |
|
30 ``'default'``). |
|
31 |
|
32 `fontface` |
|
33 The used font famliy, for example ``Bitstream Vera Sans``. Defaults to |
|
34 some generic font which is supposed to have fixed width. |
|
35 """ |
|
36 name = 'RTF' |
|
37 aliases = ['rtf'] |
|
38 filenames = ['*.rtf'] |
|
39 |
|
40 unicodeoutput = False |
|
41 |
|
42 def __init__(self, **options): |
|
43 """ |
|
44 Additional options accepted: |
|
45 |
|
46 ``fontface`` |
|
47 Name of the font used. Could for example be ``'Courier New'`` |
|
48 to further specify the default which is ``'\fmodern'``. The RTF |
|
49 specification claims that ``\fmodern`` are "Fixed-pitch serif |
|
50 and sans serif fonts". Hope every RTF implementation thinks |
|
51 the same about modern... |
|
52 """ |
|
53 Formatter.__init__(self, **options) |
|
54 self.fontface = options.get('fontface') or '' |
|
55 |
|
56 def _escape(self, text): |
|
57 return text.replace('\\', '\\\\') \ |
|
58 .replace('{', '\\{') \ |
|
59 .replace('}', '\\}') |
|
60 |
|
61 def _escape_text(self, text): |
|
62 # empty strings, should give a small performance improvment |
|
63 if not text: |
|
64 return '' |
|
65 |
|
66 # escape text |
|
67 text = self._escape(text) |
|
68 if self.encoding in ('utf-8', 'utf-16', 'utf-32'): |
|
69 encoding = 'iso-8859-15' |
|
70 else: |
|
71 encoding = self.encoding or 'iso-8859-15' |
|
72 |
|
73 buf = [] |
|
74 for c in text: |
|
75 if ord(c) > 128: |
|
76 ansic = c.encode(encoding, 'ignore') or '?' |
|
77 if ord(ansic) > 128: |
|
78 ansic = '\\\'%x' % ord(ansic) |
|
79 else: |
|
80 ansic = c |
|
81 buf.append(r'\ud{\u%d%s}' % (ord(c), ansic)) |
|
82 else: |
|
83 buf.append(str(c)) |
|
84 |
|
85 return ''.join(buf).replace('\n', '\\par\n') |
|
86 |
|
87 def format_unencoded(self, tokensource, outfile): |
|
88 # rtf 1.8 header |
|
89 outfile.write(r'{\rtf1\ansi\deff0' |
|
90 r'{\fonttbl{\f0\fmodern\fprq1\fcharset0%s;}}' |
|
91 r'{\colortbl;' % (self.fontface and |
|
92 ' ' + self._escape(self.fontface) or |
|
93 '')) |
|
94 |
|
95 # convert colors and save them in a mapping to access them later. |
|
96 color_mapping = {} |
|
97 offset = 1 |
|
98 for _, style in self.style: |
|
99 for color in style['color'], style['bgcolor'], style['border']: |
|
100 if color and color not in color_mapping: |
|
101 color_mapping[color] = offset |
|
102 outfile.write(r'\red%d\green%d\blue%d;' % ( |
|
103 int(color[0:2], 16), |
|
104 int(color[2:4], 16), |
|
105 int(color[4:6], 16) |
|
106 )) |
|
107 offset += 1 |
|
108 outfile.write(r'}\f0') |
|
109 |
|
110 # highlight stream |
|
111 for ttype, value in tokensource: |
|
112 while not self.style.styles_token(ttype) and ttype.parent: |
|
113 ttype = ttype.parent |
|
114 style = self.style.style_for_token(ttype) |
|
115 buf = [] |
|
116 if style['bgcolor']: |
|
117 buf.append(r'\cb%d' % color_mapping[style['bgcolor']]) |
|
118 if style['color']: |
|
119 buf.append(r'\cf%d' % color_mapping[style['color']]) |
|
120 if style['bold']: |
|
121 buf.append(r'\b') |
|
122 if style['italic']: |
|
123 buf.append(r'\i') |
|
124 if style['underline']: |
|
125 buf.append(r'\ul') |
|
126 if style['border']: |
|
127 buf.append(r'\chbrdr\chcfpat%d' % |
|
128 color_mapping[style['border']]) |
|
129 start = ''.join(buf) |
|
130 if start: |
|
131 outfile.write('{%s ' % start) |
|
132 outfile.write(self._escape_text(value)) |
|
133 if start: |
|
134 outfile.write('}') |
|
135 |
|
136 outfile.write('}') |