You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
4.9 KiB
146 lines
4.9 KiB
""" |
|
pygments.formatters.rtf |
|
~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
A formatter that generates RTF files. |
|
|
|
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. |
|
:license: BSD, see LICENSE for details. |
|
""" |
|
|
|
from pygments.formatter import Formatter |
|
from pygments.util import get_int_opt, surrogatepair |
|
|
|
|
|
__all__ = ['RtfFormatter'] |
|
|
|
|
|
class RtfFormatter(Formatter): |
|
""" |
|
Format tokens as RTF markup. This formatter automatically outputs full RTF |
|
documents with color information and other useful stuff. Perfect for Copy and |
|
Paste into Microsoft(R) Word(R) documents. |
|
|
|
Please note that ``encoding`` and ``outencoding`` options are ignored. |
|
The RTF format is ASCII natively, but handles unicode characters correctly |
|
thanks to escape sequences. |
|
|
|
.. versionadded:: 0.6 |
|
|
|
Additional options accepted: |
|
|
|
`style` |
|
The style to use, can be a string or a Style subclass (default: |
|
``'default'``). |
|
|
|
`fontface` |
|
The used font family, for example ``Bitstream Vera Sans``. Defaults to |
|
some generic font which is supposed to have fixed width. |
|
|
|
`fontsize` |
|
Size of the font used. Size is specified in half points. The |
|
default is 24 half-points, giving a size 12 font. |
|
|
|
.. versionadded:: 2.0 |
|
""" |
|
name = 'RTF' |
|
aliases = ['rtf'] |
|
filenames = ['*.rtf'] |
|
|
|
def __init__(self, **options): |
|
r""" |
|
Additional options accepted: |
|
|
|
``fontface`` |
|
Name of the font used. Could for example be ``'Courier New'`` |
|
to further specify the default which is ``'\fmodern'``. The RTF |
|
specification claims that ``\fmodern`` are "Fixed-pitch serif |
|
and sans serif fonts". Hope every RTF implementation thinks |
|
the same about modern... |
|
|
|
""" |
|
Formatter.__init__(self, **options) |
|
self.fontface = options.get('fontface') or '' |
|
self.fontsize = get_int_opt(options, 'fontsize', 0) |
|
|
|
def _escape(self, text): |
|
return text.replace('\\', '\\\\') \ |
|
.replace('{', '\\{') \ |
|
.replace('}', '\\}') |
|
|
|
def _escape_text(self, text): |
|
# empty strings, should give a small performance improvement |
|
if not text: |
|
return '' |
|
|
|
# escape text |
|
text = self._escape(text) |
|
|
|
buf = [] |
|
for c in text: |
|
cn = ord(c) |
|
if cn < (2**7): |
|
# ASCII character |
|
buf.append(str(c)) |
|
elif (2**7) <= cn < (2**16): |
|
# single unicode escape sequence |
|
buf.append('{\\u%d}' % cn) |
|
elif (2**16) <= cn: |
|
# RTF limits unicode to 16 bits. |
|
# Force surrogate pairs |
|
buf.append('{\\u%d}{\\u%d}' % surrogatepair(cn)) |
|
|
|
return ''.join(buf).replace('\n', '\\par\n') |
|
|
|
def format_unencoded(self, tokensource, outfile): |
|
# rtf 1.8 header |
|
outfile.write('{\\rtf1\\ansi\\uc0\\deff0' |
|
'{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}' |
|
'{\\colortbl;' % (self.fontface and |
|
' ' + self._escape(self.fontface) or |
|
'')) |
|
|
|
# convert colors and save them in a mapping to access them later. |
|
color_mapping = {} |
|
offset = 1 |
|
for _, style in self.style: |
|
for color in style['color'], style['bgcolor'], style['border']: |
|
if color and color not in color_mapping: |
|
color_mapping[color] = offset |
|
outfile.write('\\red%d\\green%d\\blue%d;' % ( |
|
int(color[0:2], 16), |
|
int(color[2:4], 16), |
|
int(color[4:6], 16) |
|
)) |
|
offset += 1 |
|
outfile.write('}\\f0 ') |
|
if self.fontsize: |
|
outfile.write('\\fs%d' % self.fontsize) |
|
|
|
# highlight stream |
|
for ttype, value in tokensource: |
|
while not self.style.styles_token(ttype) and ttype.parent: |
|
ttype = ttype.parent |
|
style = self.style.style_for_token(ttype) |
|
buf = [] |
|
if style['bgcolor']: |
|
buf.append('\\cb%d' % color_mapping[style['bgcolor']]) |
|
if style['color']: |
|
buf.append('\\cf%d' % color_mapping[style['color']]) |
|
if style['bold']: |
|
buf.append('\\b') |
|
if style['italic']: |
|
buf.append('\\i') |
|
if style['underline']: |
|
buf.append('\\ul') |
|
if style['border']: |
|
buf.append('\\chbrdr\\chcfpat%d' % |
|
color_mapping[style['border']]) |
|
start = ''.join(buf) |
|
if start: |
|
outfile.write('{%s ' % start) |
|
outfile.write(self._escape_text(value)) |
|
if start: |
|
outfile.write('}') |
|
|
|
outfile.write('}')
|
|
|